Last active
March 9, 2024 19:06
-
-
Save xennygrimmato/42a2f216de3e327f8148c6a306cfdf40 to your computer and use it in GitHub Desktop.
Generating GIFs for fuzzing a GIF Parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File 'gifdec.c' | |
Lines executed:36.91% of 317 | |
Creating 'gifdec.c.gcov' | |
File 'gifread.c' | |
Lines executed:62.50% of 56 | |
Creating 'gifread.c.gcov' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# gemini 1.5 | |
# temperature = 2.0 | |
# top_p = 0.4 | |
import random | |
import struct | |
import io | |
from typing import BinaryIO | |
RANDOM_HEADER =\ | |
b'GIF89a'\ | |
b'\x00\x01'\ | |
b'\x00\x01\x00\x00' | |
GIF_EXT_LABELS = { | |
0x01: b'text', | |
0xf9: b'graphic control', | |
0xfe: b'comment', | |
0xff: b'application', | |
} | |
RANDOM_TEXT_EXT =\ | |
b'\x01'\ | |
b'\x01\x01'\ | |
b'\x01\x01'\ | |
b'\x01\x01'\ | |
b'\x01\x01\x01\x01'\ | |
b'\x01\x01\x01\x01\x01\x01\x01\x01\x01'\ | |
b'\x01' | |
RANDOM_GCE_EXT =\ | |
b'\xf9'\ | |
b'\x04'\ | |
b'\x00' | |
RANDOM_COMMENT_EXT =\ | |
b'\xfe'\ | |
b'Comment!' | |
RANDOM_APPLICATION_EXT =\ | |
b'\xff'\ | |
b'NETSCAPE2.0'\ | |
b'\x03'\ | |
b'\x00\x00' | |
RANDOM_TERMINATOR =\ | |
b'\x00' | |
def generate_random_input(out: BinaryIO): | |
"""Generates a random file into `out`. | |
The generated input will fully exercise the parsing code. | |
Generates a random header with a random width and height, then | |
generates a random color table and a random image. Finally, generates | |
some random extensions, including the text extension, the graphic control | |
extension, the comment extension, and the application extension. | |
""" | |
out.write(RANDOM_HEADER) | |
width = random.randint(1, 1000) | |
height = random.randint(1, 1000) | |
depth = random.randint(1, 8) | |
gct_size = 1 << (depth + 1) | |
out.write(struct.pack('>H', width)) | |
out.write(struct.pack('>H', height)) | |
flags = depth | (1 << 4) | (1 << 7) | |
out.write(struct.pack('B', flags)) | |
out.write(struct.pack('B', gct_size)) | |
out.write(struct.pack('B', 0)) # background color index | |
out.write(struct.pack('B', 0)) # aspect ratio | |
# Generate a random color table. | |
for _ in range(gct_size): | |
r = random.randint(0, 255) | |
g = random.randint(0, 255) | |
b = random.randint(0, 255) | |
out.write(b"%c%c%c" % (r, g, b)) | |
# Generate a random image. | |
for _ in range(width * height): | |
index = random.randint(0, gct_size - 1) | |
color = struct.pack('B', index) | |
out.write(color) | |
# Generate some random extensions. | |
for label in GIF_EXT_LABELS.keys(): | |
if random.random() < 0.5: | |
out.write(struct.pack('B', label)) | |
if label == 0x01: | |
out.write(RANDOM_TEXT_EXT) | |
elif label == 0xf9: | |
out.write(RANDOM_GCE_EXT) | |
elif label == 0xfe: | |
out.write(RANDOM_COMMENT_EXT) | |
elif label == 0xff: | |
out.write(RANDOM_APPLICATION_EXT) | |
out.write(RANDOM_TERMINATOR) | |
out.write(RANDOM_TERMINATOR) | |
for i in range(10000): | |
with open(f"gifs/random_gif_{i}.gif", "wb") as f: | |
generate_random_input(f) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from PIL import Image, ImageDraw, ImageSequence | |
import os | |
from io import BytesIO | |
# Utility function to save GIF to a BytesIO object | |
def save_gif(images, **kwargs): | |
bio = BytesIO() | |
images[0].save(bio, format='GIF', save_all=True, append_images=images[1:], **kwargs) | |
bio.seek(0) | |
return bio | |
# Generates GIFs to test the C GIF parsing code | |
def generate_gif_inputs(): | |
# Ensure the gifs directory exists | |
os.makedirs('gifs', exist_ok=True) | |
files = [] | |
# 1. Valid GIF with a simple global color table | |
img1 = Image.new("RGB", (100, 100), "white") | |
draw = ImageDraw.Draw(img1) | |
draw.rectangle([10, 10, 90, 90], fill="blue") | |
files.append(save_gif([img1], loop=0)) | |
# 2. GIF with no global color table (invalid per parser's expectations) | |
img2 = Image.new("RGB", (100, 100), "red") | |
files.append(save_gif([img2], loop=0, global_palette=False)) | |
# 3. GIF with a NETSCAPE looping extension | |
img3 = Image.new("RGB", (50, 50), "green") | |
img3.info['loop'] = 1 # Loop once | |
files.append(save_gif([img3], loop=1)) | |
# 4. GIF with local color tables and different frames | |
img4_1 = Image.new("RGB", (100, 100), "yellow") | |
img4_2 = Image.new("RGB", (100, 100), "purple") | |
img4_2.info['local_palette'] = True | |
files.append(save_gif([img4_1, img4_2], loop=0)) | |
# 5. Interlaced GIF | |
img5 = Image.new("RGB", (100, 100), "black") | |
draw = ImageDraw.Draw(img5) | |
for y in range(0, 100, 10): | |
draw.line([(0, y), (99, y)], fill="white") | |
files.append(save_gif([img5], interlace=True)) | |
# 6. GIF with a comment extension | |
img6 = Image.new("RGB", (100, 100), "orange") | |
img6.info['comment'] = b"This is a test comment." | |
files.append(save_gif([img6])) | |
# Save all BytesIO objects to files for inspection | |
for i, file in enumerate(files): | |
with open(f'gifs/generated_gif_{i}.gif', 'wb') as f: | |
f.write(file.getvalue()) | |
generate_gif_inputs() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment