From a9f7f20eecac8102f1bc1d22f8dd7e3cc9a92e27 Mon Sep 17 00:00:00 2001 From: fuzzing-researcher Date: Fri, 7 Aug 2026 21:34:52 +0300 Subject: [PATCH 1/3] Guard memcpy against NULL source on first resize On the first quirc_resize() the previous image buffer q->image is NULL and q->w == q->h == 0, so min == 0 and the copy becomes memcpy(image, NULL, 0). That is undefined behaviour (memcpy's source is declared nonnull) and is flagged by UBSan on the first decode of every fresh quirc handle. Skip the copy when there is nothing to copy. --- lib/quirc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/quirc.c b/lib/quirc.c index 3cf75b9..8379f6e 100644 --- a/lib/quirc.c +++ b/lib/quirc.c @@ -81,7 +81,8 @@ int quirc_resize(struct quirc *q, int w, int h) * old buffer when the new size is greater and (b) to write beyond the * new buffer when the new size is smaller, hence the min computation. */ - (void)memcpy(image, q->image, min); + if (min) + (void)memcpy(image, q->image, min); /* alloc a new buffer for q->pixels if needed */ if (!QUIRC_PIXEL_ALIAS_IMAGE) { From 081d754b9cf6d91adaada586f1db2d01b4690abd Mon Sep 17 00:00:00 2001 From: fuzzing-researcher Date: Fri, 7 Aug 2026 21:34:52 +0300 Subject: [PATCH 2/3] Bound alignment-pattern spiral search (DoS on crafted image) find_alignment_pattern() spirals outward while step_size*step_size < size_estimate*100. size_estimate is derived from coordinates produced by the perspective transform; a degenerate transform (near-zero denominator) makes those coordinates huge, so size_estimate becomes enormous and the loop runs for hundreds of millions of iterations. A 246x246 image (about the size of a real screenshot QR) makes quirc_end() run for over 90 seconds on ~28 MB of RAM - a pure CPU hang on attacker-supplied image data. The alignment pattern must lie within the image, so cap the search radius at the image size. Verified with a fuzzer-found reproducer: the 90s+ hang drops to ~12 ms and real QR codes still decode. Found by fuzzing (libFuzzer + ASan/UBSan). --- lib/identify.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/identify.c b/lib/identify.c index b98877b..fdb96ef 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -605,8 +605,15 @@ static void find_alignment_pattern(struct quirc *q, int index) /* Spiral outwards from the estimate point until we find something * roughly the right size. Don't look too far from the estimate * point. + * + * A degenerate perspective transform (near-zero denominator) can make + * size_estimate enormous, spinning this spiral for hundreds of millions + * of iterations on a tiny image (denial of service on a crafted QR). The + * alignment pattern must lie within the image, so also cap the search + * radius at the image size. */ - while (step_size * step_size < size_estimate * 100) { + int max_step = (q->w > q->h ? q->w : q->h) + 1; + while (step_size * step_size < size_estimate * 100 && step_size <= max_step) { static const int dx_map[] = {1, 0, -1, 0}; static const int dy_map[] = {0, -1, 0, 1}; int i; From b1d371fc48561a0b1a1255364b307bb8a080b15a Mon Sep 17 00:00:00 2001 From: AlexandrKhromov2005 <146848229+AlexandrKhromov2005@users.noreply.github.com> Date: Wed, 19 Aug 2026 21:49:17 +0300 Subject: [PATCH 3/3] identify, quirc: address review nits (const max_step, explicit min > 0) --- lib/identify.c | 2 +- lib/quirc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index fdb96ef..aa9c1d5 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -612,7 +612,7 @@ static void find_alignment_pattern(struct quirc *q, int index) * alignment pattern must lie within the image, so also cap the search * radius at the image size. */ - int max_step = (q->w > q->h ? q->w : q->h) + 1; + const int max_step = (q->w > q->h ? q->w : q->h) + 1; while (step_size * step_size < size_estimate * 100 && step_size <= max_step) { static const int dx_map[] = {1, 0, -1, 0}; static const int dy_map[] = {0, -1, 0, 1}; diff --git a/lib/quirc.c b/lib/quirc.c index 8379f6e..4e22f8e 100644 --- a/lib/quirc.c +++ b/lib/quirc.c @@ -81,7 +81,7 @@ int quirc_resize(struct quirc *q, int w, int h) * old buffer when the new size is greater and (b) to write beyond the * new buffer when the new size is smaller, hence the min computation. */ - if (min) + if (min > 0) (void)memcpy(image, q->image, min); /* alloc a new buffer for q->pixels if needed */