Skip to content

Commit 5fd87f4

Browse files
committed
loader/coregraphics: reject oversized indexed provider copy
Prevent indexed decode from requesting oversized provider copies and add a minimized regression payload test.
1 parent 7b4e2d3 commit 5fd87f4

File tree

3 files changed

+152
-7
lines changed

3 files changed

+152
-7
lines changed

src/loader-coregraphics.c

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,14 +1155,34 @@ coregraphics_copy_indexed_pixels(CGImageRef image,
11551155
return SIXEL_BAD_INTEGER_OVERFLOW;
11561156
}
11571157
needed_bytes = (size_t)height * bytes_per_row;
1158+
if (needed_bytes > (size_t)SIXEL_ALLOCATE_BYTES_MAX) {
1159+
sixel_helper_set_additional_message(
1160+
"load_with_coregraphics: indexed source row payload is too "
1161+
"large.");
1162+
return SIXEL_BAD_INPUT;
1163+
}
1164+
1165+
if ((size_t)width > SIZE_MAX / (size_t)height) {
1166+
status = SIXEL_BAD_INTEGER_OVERFLOW;
1167+
goto cleanup;
1168+
}
1169+
pixel_count = (size_t)width * (size_t)height;
1170+
if (pixel_count > (size_t)SIXEL_ALLOCATE_BYTES_MAX) {
1171+
sixel_helper_set_additional_message(
1172+
"load_with_coregraphics: indexed image is too large.");
1173+
status = SIXEL_BAD_INPUT;
1174+
goto cleanup;
1175+
}
11581176

11591177
provider = CGImageGetDataProvider(image);
11601178
if (provider == NULL) {
1161-
return SIXEL_FALSE;
1179+
status = SIXEL_FALSE;
1180+
goto cleanup;
11621181
}
11631182
provider_data = CGDataProviderCopyData(provider);
11641183
if (provider_data == NULL) {
1165-
return SIXEL_FALSE;
1184+
status = SIXEL_FALSE;
1185+
goto cleanup;
11661186
}
11671187
src = CFDataGetBytePtr(provider_data);
11681188
data_size = (size_t)CFDataGetLength(provider_data);
@@ -1171,11 +1191,6 @@ coregraphics_copy_indexed_pixels(CGImageRef image,
11711191
goto cleanup;
11721192
}
11731193

1174-
if ((size_t)width > SIZE_MAX / (size_t)height) {
1175-
status = SIXEL_BAD_INTEGER_OVERFLOW;
1176-
goto cleanup;
1177-
}
1178-
pixel_count = (size_t)width * (size_t)height;
11791194
pixels = (unsigned char *)sixel_allocator_malloc(allocator, pixel_count);
11801195
if (pixels == NULL) {
11811196
sixel_helper_set_additional_message(

tests/loader/coregraphics/0008_loader_coregraphics_pixelformat.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ typedef struct coregraphics_animation_probe {
3030
#define WEBP_LOOP2_IMAGE_PATH \
3131
"/tests/data/inputs/formats/animated-lossless-8x8-2frame-loop2-min.webp"
3232

33+
/*
34+
* Minimized fuzz payload that previously forced CoreGraphics indexed decoding
35+
* into a 4 GiB provider copy request.
36+
*/
37+
static unsigned char const coregraphics_indexed_provider_oom_poc[33] = {
38+
0x42, 0x4d, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00,
39+
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00,
40+
0x00, 0x00, 0x02, 0xff, 0xd8, 0xff, 0x01, 0x00,
41+
0x01, 0x00, 0x00, 0xbf, 0xff, 0x40, 0x00, 0x00,
42+
0x00
43+
};
44+
3345
typedef struct coregraphics_loop_sequence_probe {
3446
int callback_count;
3547
int expected_count;
@@ -2136,6 +2148,98 @@ run_coregraphics_cfindex_size_overflow_reject_test(void)
21362148
return SIXEL_FAILED(status) ? 1 : 0;
21372149
}
21382150

2151+
static int
2152+
run_coregraphics_indexed_provider_copy_oom_reject_test(void)
2153+
{
2154+
SIXELSTATUS status;
2155+
sixel_allocator_t *allocator;
2156+
sixel_loader_component_t *component;
2157+
sixel_chunk_t chunk;
2158+
int require_static;
2159+
int use_palette;
2160+
int reqcolors;
2161+
loader_probe_callback_state_t callback_state;
2162+
coregraphics_callback_count_probe_t probe;
2163+
2164+
status = SIXEL_FALSE;
2165+
allocator = NULL;
2166+
component = NULL;
2167+
memset(&chunk, 0, sizeof(chunk));
2168+
require_static = 0;
2169+
use_palette = 1;
2170+
reqcolors = 256;
2171+
callback_state.loader = NULL;
2172+
callback_state.fn = NULL;
2173+
callback_state.context = NULL;
2174+
probe.callback_count = 0;
2175+
2176+
status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
2177+
if (SIXEL_FAILED(status)) {
2178+
fprintf(stderr, "coregraphics: allocator initialization failed\n");
2179+
return 1;
2180+
}
2181+
2182+
status = new_coregraphics_component(allocator, &component);
2183+
if (SIXEL_FAILED(status)) {
2184+
fprintf(stderr,
2185+
"coregraphics: component init failed (%d)\n",
2186+
(int)status);
2187+
goto cleanup;
2188+
}
2189+
2190+
status = sixel_loader_component_setopt(component,
2191+
SIXEL_LOADER_OPTION_REQUIRE_STATIC,
2192+
&require_static);
2193+
if (SIXEL_FAILED(status)) {
2194+
goto cleanup;
2195+
}
2196+
status = sixel_loader_component_setopt(component,
2197+
SIXEL_LOADER_OPTION_USE_PALETTE,
2198+
&use_palette);
2199+
if (SIXEL_FAILED(status)) {
2200+
goto cleanup;
2201+
}
2202+
status = sixel_loader_component_setopt(component,
2203+
SIXEL_LOADER_OPTION_REQCOLORS,
2204+
&reqcolors);
2205+
if (SIXEL_FAILED(status)) {
2206+
goto cleanup;
2207+
}
2208+
2209+
chunk.buffer = (unsigned char *)(uintptr_t)
2210+
coregraphics_indexed_provider_oom_poc;
2211+
chunk.size = sizeof(coregraphics_indexed_provider_oom_poc);
2212+
chunk.max_size = sizeof(coregraphics_indexed_provider_oom_poc);
2213+
chunk.source_path = NULL;
2214+
chunk.allocator = allocator;
2215+
2216+
callback_state.fn = capture_coregraphics_callback_count;
2217+
callback_state.context = &probe;
2218+
status = sixel_loader_component_load(component,
2219+
&chunk,
2220+
capture_frame_trampoline,
2221+
&callback_state);
2222+
if (status != SIXEL_BAD_INPUT) {
2223+
fprintf(stderr,
2224+
"coregraphics: expected bad-input status, got %d\n",
2225+
(int)status);
2226+
status = SIXEL_FALSE;
2227+
goto cleanup;
2228+
}
2229+
if (probe.callback_count != 0) {
2230+
fprintf(stderr,
2231+
"coregraphics: callback was invoked on indexed oom input\n");
2232+
status = SIXEL_FALSE;
2233+
goto cleanup;
2234+
}
2235+
status = SIXEL_OK;
2236+
2237+
cleanup:
2238+
sixel_loader_component_unref(component);
2239+
sixel_allocator_unref(allocator);
2240+
return SIXEL_FAILED(status) ? 1 : 0;
2241+
}
2242+
21392243
static int
21402244
run_coregraphics_cache_invalid_env_reject_test(void)
21412245
{
@@ -2227,6 +2331,8 @@ run_coregraphics_loader_test(void)
22272331
run_coregraphics_huge_dimension_metadata_reject_test },
22282332
{ "SIXEL_TEST_COREGRAPHICS_CFINDEX_SIZE_OVERFLOW_REJECT",
22292333
run_coregraphics_cfindex_size_overflow_reject_test },
2334+
{ "SIXEL_TEST_COREGRAPHICS_INDEXED_PROVIDER_COPY_OOM_REJECT",
2335+
run_coregraphics_indexed_provider_copy_oom_reject_test },
22302336
{ "SIXEL_TEST_COREGRAPHICS_CACHE_INVALID_ENV_REJECT",
22312337
run_coregraphics_cache_invalid_env_reject_test }
22322338
};
@@ -2290,6 +2396,10 @@ run_coregraphics_loader_test(void)
22902396
if (result != 0) {
22912397
return result;
22922398
}
2399+
result = run_coregraphics_indexed_provider_copy_oom_reject_test();
2400+
if (result != 0) {
2401+
return result;
2402+
}
22932403
return 0;
22942404
}
22952405
#endif
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/sh
2+
set -eux
3+
4+
test "${HAVE_COREGRAPHICS-}" = 1 || {
5+
printf "1..0 # SKIP coregraphics loader is unavailable\n"
6+
exit 0
7+
}
8+
9+
echo "1..1"
10+
set -v
11+
12+
${SIXEL_RUNTIME-} "${TEST_RUNNER_PATH}" \
13+
--env "SIXEL_TEST_COREGRAPHICS_INDEXED_PROVIDER_COPY_OOM_REJECT=1" \
14+
"loader/0008_loader_coregraphics_pixelformat" || {
15+
echo "not ok 1 - loader/0008_loader_coregraphics_pixelformat (indexed provider copy oom reject)"
16+
exit 0
17+
}
18+
19+
echo "ok 1 - loader/0008_loader_coregraphics_pixelformat (indexed provider copy oom reject)"
20+
exit 0

0 commit comments

Comments
 (0)