Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions src/ImageSharp/Formats/Pbm/BufferedReadStreamExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO;

namespace SixLabors.ImageSharp.Formats.Pbm;
Expand Down Expand Up @@ -28,16 +29,78 @@ public static void SkipWhitespaceAndComments(this BufferedReadStream stream)
{
innerValue = stream.ReadByte();
}
while (innerValue != 0x0a);
while (!IsNewlineOrEndOfStream(innerValue));

// Continue searching for whitespace.
val = innerValue;
}

isWhitespace = val is 0x09 or 0x0a or 0x0d or 0x20;
isWhitespace = IsWhitespace(val);
}
while (isWhitespace);
stream.Seek(-1, SeekOrigin.Current);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool IsNewlineOrEndOfStream(int val)
{
// See comment below for how this works
if (Environment.Is64BitProcess)
{
const ulong magicConstant = 0x8010000000000000UL;

ulong i = (uint)val + 0x1; // - -0x1 -> + 0x1
ulong shift = magicConstant << (int)i;
ulong mask = i - 64;

return (long)(shift & mask) < 0;
}

return val is 0x0a or -0x1;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool IsWhitespace(int val)
{
// The magic constant 0xC800010000000000 is a 64 bit value containing 1s at the indices
// of all whitespace chars minus 0x09 in a backwards order (from the MSB downwards).
// The subtraction of 0x09 is necessary so that the entire range fits in 64 bits.
//
// From the input 0x09 is subtracted, then it's zero-extended to ulong, meaning the upper
// 32 bits are 0. Then the constant is left shifted by that offset.
// A bitmask, that has the sign bit (the highest bit) set iff 'val' is in the [0x09, 0x09 + 64) range,
// is applied. Thus we only need to check if the final result is < 0, which will only be the case if
// 'i' was the index of a set bit in the magic constant, and if val was in the allowed range.
/* Constant created with
using System;
using System.Linq;

int[] chars = { 0x09, 0x0a, 0x0d, 0x20 };
int min = chars.Min();
ulong magic = 0;

foreach (int c in chars)
{
int idx = c - min;
magic |= 1UL << (64 - 1 - idx);
}

Console.WriteLine(magic);
Console.WriteLine($"0x{magic:X16}");
*/

if (Environment.Is64BitProcess)
{
const ulong magicConstant = 0xC800010000000000UL;

ulong i = (uint)val - 0x09;
ulong shift = magicConstant << (int)i;
ulong mask = i - 64;

return (long)(shift & mask) < 0;
}

return val is 0x09 or 0x0a or 0x0d or 0x20;
}
}

/// <summary>
Expand Down
15 changes: 14 additions & 1 deletion src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ private void StoreFullHuffmanCode(Span<HuffmanTree> huffTree, HuffmanTreeToken[]
while (i-- > 0)
{
int ix = tokens[i].Code;
if (ix is 0 or 17 or 18)
if (Is0Or17Or18(ix))
{
trimmedLength--; // Discount trailing zeros.
trailingZeroBits += codeLengthBitDepth[ix];
Expand All @@ -934,6 +934,19 @@ private void StoreFullHuffmanCode(Span<HuffmanTree> huffTree, HuffmanTreeToken[]
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool Is0Or17Or18(int val)
{
// See comment in BufferedReadStreamExtensions on how this works.
const ulong magicConstant = 0x8000600000000000UL;

ulong i = (uint)val;
ulong shift = magicConstant << (int)i;
ulong mask = i - 64;

return (long)(shift & mask) < 0;
}

bool writeTrimmedLength = trimmedLength > 1 && trailingZeroBits > 12;
int length = writeTrimmedLength ? trimmedLength : numTokens;
this.bitWriter.PutBits((uint)(writeTrimmedLength ? 1 : 0), 1);
Expand Down
11 changes: 11 additions & 0 deletions tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Pbm;
using static SixLabors.ImageSharp.Tests.TestImages.Pbm;

Expand Down Expand Up @@ -80,4 +81,14 @@ public void Identify_DetectsCorrectComponentType(string imagePath, PbmComponentT
Assert.NotNull(bitmapMetadata);
Assert.Equal(expectedComponentType, bitmapMetadata.ComponentType);
}

[Fact]
public void Identify_HandlesCraftedDenialOfServiceString()
{
byte[] bytes = Convert.FromBase64String("UDEjWAAACQAAAAA=");
ImageInfo info = Image.Identify(bytes);
Assert.Equal(default, info.Size);
Configuration.Default.ImageFormatsManager.TryFindFormatByFileExtension("pbm", out IImageFormat format);
Assert.Equal(format!, info.Metadata.DecodedImageFormat);
}
}