Skip to content

Commit 85e060d

Browse files
committed
Add missing sample
1 parent 71deb32 commit 85e060d

1 file changed

Lines changed: 162 additions & 0 deletions

File tree

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/* Regression tests for libretro-common/formats/png/rpng.c
2+
*
3+
* Exercises the chunk-header phase of rpng_iterate_image after
4+
* the integer-overflow hardening in the accompanying patch.
5+
*
6+
* Honest note on discriminator status: the headline bug fixed
7+
* here is a pointer-arithmetic overflow in
8+
* if (buf + 8 + chunk_size > rpng->buff_end) return false;
9+
* that is only genuinely reachable on 32-bit builds -- on a
10+
* 64-bit host the pointer has enough headroom that the compare
11+
* still returns the right answer (the arithmetic itself is UB
12+
* per C99, but no sanitizer flags it in practice on 64-bit).
13+
* These tests therefore exercise the NEW size_t-based guard and
14+
* serve as regression protection against anyone reintroducing
15+
* unguarded pointer arithmetic; they are not pre/post
16+
* discriminators on a 64-bit CI host.
17+
*
18+
* Subtests:
19+
* 1. chunk_size near UINT32_MAX is rejected.
20+
* 2. chunk_size larger than remaining input is rejected.
21+
* 3. Valid minimal PNG with just IHDR iterates cleanly.
22+
*/
23+
24+
#include <stdio.h>
25+
#include <stdlib.h>
26+
#include <string.h>
27+
#include <stdint.h>
28+
29+
#include <formats/rpng.h>
30+
#include <formats/image.h>
31+
32+
static int failures = 0;
33+
34+
static void put32be(uint8_t *p, uint32_t v)
35+
{
36+
p[0] = (uint8_t)(v >> 24);
37+
p[1] = (uint8_t)(v >> 16);
38+
p[2] = (uint8_t)(v >> 8);
39+
p[3] = (uint8_t)(v & 0xFF);
40+
}
41+
42+
/* Minimal PNG magic. */
43+
static const uint8_t png_magic[8] = {
44+
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A
45+
};
46+
47+
static void test_chunk_size_uint32_max(void)
48+
{
49+
/* PNG with a single chunk declaring chunk_size = 0xFFFFFFF8.
50+
* On 32-bit pre-patch this defeated the pointer compare and
51+
* the IDAT handler's memcpy could read up to ~4 GiB past
52+
* the end of the input. Post-patch the size compare rejects
53+
* on any word size. */
54+
uint8_t file[64];
55+
rpng_t *rpng;
56+
bool iter;
57+
size_t off = 0;
58+
59+
memcpy(file + off, png_magic, 8); off += 8;
60+
put32be(file + off, 0xFFFFFFF8u); off += 4; /* size */
61+
memcpy(file + off, "IHDR", 4); off += 4; /* type */
62+
memset(file + off, 0xAA, sizeof(file) - off);
63+
64+
rpng = rpng_alloc();
65+
if (!rpng) { printf("[ERROR] alloc\n"); failures++; return; }
66+
rpng_set_buf_ptr(rpng, file, sizeof(file));
67+
rpng_start(rpng);
68+
iter = rpng_iterate_image(rpng);
69+
if (iter)
70+
{
71+
printf("[ERROR] chunk_size=0xFFFFFFF8 was accepted\n");
72+
failures++;
73+
}
74+
else
75+
printf("[SUCCESS] chunk_size=0xFFFFFFF8 rejected\n");
76+
rpng_free(rpng);
77+
}
78+
79+
static void test_chunk_size_larger_than_input(void)
80+
{
81+
/* chunk_size = 1000 with only 40 bytes of input. Must
82+
* reject -- the size compare catches this on all hosts. */
83+
uint8_t file[40];
84+
rpng_t *rpng;
85+
bool iter;
86+
size_t off = 0;
87+
88+
memcpy(file + off, png_magic, 8); off += 8;
89+
put32be(file + off, 1000); off += 4;
90+
memcpy(file + off, "zzzz", 4); off += 4;
91+
memset(file + off, 0, sizeof(file) - off);
92+
93+
rpng = rpng_alloc();
94+
rpng_set_buf_ptr(rpng, file, sizeof(file));
95+
rpng_start(rpng);
96+
iter = rpng_iterate_image(rpng);
97+
if (iter)
98+
{
99+
printf("[ERROR] chunk_size=1000 in 40-byte file accepted\n");
100+
failures++;
101+
}
102+
else
103+
printf("[SUCCESS] oversized chunk_size rejected\n");
104+
rpng_free(rpng);
105+
}
106+
107+
static void test_happy_ihdr(void)
108+
{
109+
/* Minimal valid framing: magic + IHDR (13 bytes) + space for
110+
* CRC. iterate_image should return true for IHDR. */
111+
uint8_t file[64];
112+
uint8_t ihdr[13];
113+
rpng_t *rpng;
114+
size_t off = 0;
115+
bool iter;
116+
117+
memset(file, 0, sizeof(file));
118+
memcpy(file + off, png_magic, 8); off += 8;
119+
120+
/* IHDR body: 8x8 RGBA-8. */
121+
put32be(ihdr + 0, 8); /* width */
122+
put32be(ihdr + 4, 8); /* height */
123+
ihdr[8] = 8; /* depth = 8 */
124+
ihdr[9] = 6; /* color_type = RGBA */
125+
ihdr[10] = 0; /* compression */
126+
ihdr[11] = 0; /* filter */
127+
ihdr[12] = 0; /* interlace */
128+
129+
put32be(file + off, 13); off += 4; /* chunk size */
130+
memcpy(file + off, "IHDR", 4); off += 4; /* type */
131+
memcpy(file + off, ihdr, 13); off += 13; /* data */
132+
/* 4-byte CRC space (value doesn't matter, not verified here) */
133+
memset(file + off, 0, 4); off += 4;
134+
135+
rpng = rpng_alloc();
136+
rpng_set_buf_ptr(rpng, file, sizeof(file));
137+
rpng_start(rpng);
138+
iter = rpng_iterate_image(rpng);
139+
if (!iter)
140+
{
141+
printf("[ERROR] IHDR iteration failed unexpectedly\n");
142+
failures++;
143+
}
144+
else
145+
printf("[SUCCESS] IHDR chunk iterated successfully\n");
146+
rpng_free(rpng);
147+
}
148+
149+
int main(void)
150+
{
151+
test_chunk_size_uint32_max();
152+
test_chunk_size_larger_than_input();
153+
test_happy_ihdr();
154+
155+
if (failures)
156+
{
157+
printf("\n%d rpng chunk-overflow test(s) failed\n", failures);
158+
return 1;
159+
}
160+
printf("\nAll rpng chunk-overflow regression tests passed.\n");
161+
return 0;
162+
}

0 commit comments

Comments
 (0)