Skip to content

Commit a58bfae

Browse files
authored
validate poppler page size (#4620)
* validate poppler page size Poppler can return pages less than 1 pixel wide or high. Check for this and flag an error. Thanks Yang Luo, Riema Labs * validate page size in svgload too * add image sanity tests * make vips_object_sanity() log an error
1 parent 693d7fc commit a58bfae

File tree

8 files changed

+82
-66
lines changed

8 files changed

+82
-66
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
8.17.2
22

33
- rank: fix an off-by-one error [larsmaxfield]
4+
- popplerload, svgload: validate page size [Yang Luo]
45
- pdfiumload: allow both dpi and scale to be set [kleisauke]
56

67
7/7/25 8.17.1

libvips/foreign/foreign.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,7 @@ vips_foreign_load_start(VipsImage *out, void *a, void *b)
10401040
* If the load fails, we need to stop.
10411041
*/
10421042
if (class->load(load) ||
1043+
!vips_object_sanity(VIPS_OBJECT(load->real)) ||
10431044
vips_image_pio_input(load->real) ||
10441045
!vips_foreign_load_iscompat(load->real, out)) {
10451046
vips_operation_invalidate(VIPS_OPERATION(load));
@@ -1133,18 +1134,19 @@ vips_foreign_load_build(VipsObject *object)
11331134

11341135
g_object_set(object, "out", vips_image_new(), NULL);
11351136

1136-
vips_image_set_string(load->out,
1137-
VIPS_META_LOADER, class->nickname);
1137+
vips_image_set_string(load->out, VIPS_META_LOADER, class->nickname);
11381138

11391139
#ifdef DEBUG
11401140
printf("vips_foreign_load_build: triggering ->header\n");
11411141
#endif /*DEBUG*/
11421142

11431143
/* Read the header into @out.
11441144
*/
1145-
if (fclass->header &&
1146-
fclass->header(load))
1147-
return -1;
1145+
if (fclass->header) {
1146+
if (fclass->header(load) ||
1147+
!vips_object_sanity(VIPS_OBJECT(load->out)))
1148+
return -1;
1149+
}
11481150

11491151
/* If there's no ->load() method then the header read has done
11501152
* everything. Otherwise, it's just set fields and we must also

libvips/foreign/popplerload.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,6 @@ vips_foreign_load_pdf_header(VipsForeignLoad *load)
312312
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF(load);
313313

314314
int top;
315-
int i;
316315

317316
#ifdef DEBUG
318317
printf("vips_foreign_load_pdf_header: %p\n", pdf);
@@ -342,21 +341,28 @@ vips_foreign_load_pdf_header(VipsForeignLoad *load)
342341
pdf->image.top = 0;
343342
pdf->image.width = 0;
344343
pdf->image.height = 0;
345-
for (i = 0; i < pdf->n; i++) {
344+
for (int i = 0; i < pdf->n; i++) {
346345
double width;
347346
double height;
348347

349348
if (vips_foreign_load_pdf_get_page(pdf, pdf->page_no + i))
350349
return -1;
350+
351351
poppler_page_get_size(pdf->page, &width, &height);
352352
pdf->pages[i].left = 0;
353353
pdf->pages[i].top = top;
354+
354355
/* We do round to nearest, in the same way that vips_resize()
355356
* does round to nearest. Without this, things like
356357
* shrink-on-load will break.
357358
*/
358359
pdf->pages[i].width = rint(width * pdf->total_scale);
359360
pdf->pages[i].height = rint(height * pdf->total_scale);
361+
if (pdf->pages[i].width <= 0 ||
362+
pdf->pages[i].height <= 0) {
363+
vips_error(class->nickname, "%s", _("zero-sized image"));
364+
return -1;
365+
}
360366

361367
if (pdf->pages[i].width > pdf->image.width)
362368
pdf->image.width = pdf->pages[i].width;
@@ -368,7 +374,7 @@ vips_foreign_load_pdf_header(VipsForeignLoad *load)
368374
/* If all pages are the same height, we can tag this as a toilet roll
369375
* image.
370376
*/
371-
for (i = 1; i < pdf->n; i++)
377+
for (int i = 1; i < pdf->n; i++)
372378
if (pdf->pages[i].height != pdf->pages[0].height)
373379
break;
374380

libvips/foreign/svgload.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,12 +568,19 @@ vips_foreign_load_svg_get_scaled_size(VipsForeignLoadSvg *svg,
568568
static int
569569
vips_foreign_load_svg_parse(VipsForeignLoadSvg *svg, VipsImage *out)
570570
{
571+
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(svg);
572+
571573
int width;
572574
int height;
573575
double res;
574576

575577
if (vips_foreign_load_svg_get_scaled_size(svg, &width, &height))
576578
return -1;
579+
if (width <= 0 ||
580+
height <= 0) {
581+
vips_error(class->nickname, "%s", _("zero-sized image"));
582+
return -1;
583+
}
577584

578585
/* We need pixels/mm for vips.
579586
*/
@@ -583,7 +590,8 @@ vips_foreign_load_svg_parse(VipsForeignLoadSvg *svg, VipsImage *out)
583590
width, height, 4,
584591
svg->high_bitdepth ? VIPS_FORMAT_FLOAT : VIPS_FORMAT_UCHAR,
585592
VIPS_CODING_NONE,
586-
svg->high_bitdepth ? VIPS_INTERPRETATION_scRGB : VIPS_INTERPRETATION_sRGB,
593+
svg->high_bitdepth ?
594+
VIPS_INTERPRETATION_scRGB : VIPS_INTERPRETATION_sRGB,
587595
res, res);
588596

589597
/* We use a tilecache, so it's smalltile.

libvips/iofuncs/generate.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,8 @@ vips__demand_hint_array(VipsImage *image,
297297
*/
298298
set_hint = hint;
299299
for (i = 0; i < len; i++)
300-
set_hint = (VipsDemandStyle) VIPS_MIN(
301-
(int) set_hint, (int) in[i]->dhint);
300+
set_hint = (VipsDemandStyle)
301+
VIPS_MIN((int) set_hint, (int) in[i]->dhint);
302302

303303
image->dhint = set_hint;
304304

@@ -365,6 +365,9 @@ int
365365
vips_image_pipeline_array(VipsImage *image,
366366
VipsDemandStyle hint, VipsImage **in)
367367
{
368+
if (!vips_object_sanity(VIPS_OBJECT(image)))
369+
return -1;
370+
368371
/* This function can be called more than once per output image. For
369372
* example, jpeg header load will call this once on ->out to set the
370373
* default hint, then later call it again to connect the output image
@@ -688,11 +691,12 @@ vips_image_generate(VipsImage *image,
688691
VIPS_DEBUG_MSG("vips_image_generate: %p\n", image);
689692

690693
g_assert(generate_fn);
691-
g_assert(vips_object_sanity(VIPS_OBJECT(image)));
694+
695+
if (!vips_object_sanity(VIPS_OBJECT(image)))
696+
return -1;
692697

693698
if (!image->hint_set) {
694-
vips_error("vips_image_generate",
695-
"%s", _("demand hint not set"));
699+
vips_error("vips_image_generate", "%s", _("demand hint not set"));
696700
return -1;
697701
}
698702

libvips/iofuncs/image.c

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -615,8 +615,7 @@ vips_image_summary(VipsObject *object, VipsBuf *buf)
615615
if (vips_image_get_coding(image) == VIPS_CODING_NONE) {
616616
vips_buf_appendf(buf,
617617
g_dngettext(GETTEXT_PACKAGE,
618-
" %s, %d band, %s",
619-
" %s, %d bands, %s",
618+
" %s, %d band, %s", " %s, %d bands, %s",
620619
vips_image_get_bands(image)),
621620
vips_enum_nick(VIPS_TYPE_BAND_FORMAT, vips_image_get_format(image)),
622621
vips_image_get_bands(image),
@@ -625,8 +624,7 @@ vips_image_summary(VipsObject *object, VipsBuf *buf)
625624
}
626625
else {
627626
vips_buf_appendf(buf, ", %s",
628-
vips_enum_nick(VIPS_TYPE_CODING,
629-
vips_image_get_coding(image)));
627+
vips_enum_nick(VIPS_TYPE_CODING, vips_image_get_coding(image)));
630628
}
631629

632630
if (vips_image_get_typeof(image, VIPS_META_LOADER) &&
@@ -657,42 +655,40 @@ vips_image_sanity(VipsObject *object, VipsBuf *buf)
657655
{
658656
VipsImage *image = VIPS_IMAGE(object);
659657

660-
/* All 0 means im has been inited but never used.
661-
*/
662-
if (image->Xsize != 0 ||
663-
image->Ysize != 0 ||
664-
image->Bands != 0) {
665-
if (image->Xsize <= 0 ||
666-
image->Ysize <= 0 ||
667-
image->Bands <= 0)
668-
vips_buf_appends(buf, "bad dimensions\n");
669-
if (image->BandFmt < -1 ||
670-
image->BandFmt > VIPS_FORMAT_DPCOMPLEX ||
671-
(image->Coding != -1 &&
672-
image->Coding != VIPS_CODING_NONE &&
673-
image->Coding != VIPS_CODING_LABQ &&
674-
image->Coding != VIPS_CODING_RAD) ||
675-
image->Type >= VIPS_INTERPRETATION_LAST ||
676-
image->dtype > VIPS_IMAGE_PARTIAL ||
677-
image->dhint > VIPS_DEMAND_STYLE_ANY)
678-
vips_buf_appends(buf, "bad enum\n");
679-
if (image->Xres < 0 ||
680-
image->Yres < 0)
681-
vips_buf_appends(buf, "bad resolution\n");
682-
}
683-
684-
/* Must lock around inter-image links.
658+
if (image->Xsize <= 0 ||
659+
image->Ysize <= 0 ||
660+
image->Bands <= 0)
661+
vips_buf_appends(buf, "bad dimensions\n");
662+
if (image->BandFmt < -1 ||
663+
image->BandFmt > VIPS_FORMAT_DPCOMPLEX ||
664+
(image->Coding != -1 &&
665+
image->Coding != VIPS_CODING_NONE &&
666+
image->Coding != VIPS_CODING_LABQ &&
667+
image->Coding != VIPS_CODING_RAD) ||
668+
image->Type >= VIPS_INTERPRETATION_LAST ||
669+
image->dtype > VIPS_IMAGE_PARTIAL ||
670+
image->dhint > VIPS_DEMAND_STYLE_ANY)
671+
vips_buf_appends(buf, "bad enum\n");
672+
if (image->Xres < 0 ||
673+
image->Yres < 0)
674+
vips_buf_appends(buf, "bad resolution\n");
675+
676+
/* These checks are expensive -- only do in leakcheck mode.
685677
*/
686-
g_mutex_lock(&vips__global_lock);
678+
if (vips__leak) {
679+
/* Must lock around inter-image links.
680+
*/
681+
g_mutex_lock(&vips__global_lock);
687682

688-
if (vips_slist_map2(image->upstream,
689-
(VipsSListMap2Fn) vips_image_sanity_upstream, image, NULL))
690-
vips_buf_appends(buf, "upstream broken\n");
691-
if (vips_slist_map2(image->downstream,
692-
(VipsSListMap2Fn) vips_image_sanity_downstream, image, NULL))
693-
vips_buf_appends(buf, "downstream broken\n");
683+
if (vips_slist_map2(image->upstream,
684+
(VipsSListMap2Fn) vips_image_sanity_upstream, image, NULL))
685+
vips_buf_appends(buf, "upstream broken\n");
686+
if (vips_slist_map2(image->downstream,
687+
(VipsSListMap2Fn) vips_image_sanity_downstream, image, NULL))
688+
vips_buf_appends(buf, "downstream broken\n");
694689

695-
g_mutex_unlock(&vips__global_lock);
690+
g_mutex_unlock(&vips__global_lock);
691+
}
696692

697693
VIPS_OBJECT_CLASS(vips_image_parent_class)->sanity(object, buf);
698694
}
@@ -3148,7 +3144,8 @@ vips_image_hasalpha(VipsImage *image)
31483144
int
31493145
vips_image_write_prepare(VipsImage *image)
31503146
{
3151-
g_assert(vips_object_sanity(VIPS_OBJECT(image)));
3147+
if (!vips_object_sanity(VIPS_OBJECT(image)))
3148+
return -1;
31523149

31533150
if (image->Xsize <= 0 ||
31543151
image->Ysize <= 0 ||
@@ -3416,7 +3413,8 @@ vips_image_wio_input(VipsImage *image)
34163413
{
34173414
VipsImage *t1;
34183415

3419-
g_assert(vips_object_sanity(VIPS_OBJECT(image)));
3416+
if (!vips_object_sanity(VIPS_OBJECT(image)))
3417+
return -1;
34203418

34213419
#ifdef DEBUG_IO
34223420
printf("vips_image_wio_input: wio input for %s\n",
@@ -3644,7 +3642,8 @@ vips_image_inplace(VipsImage *image)
36443642
int
36453643
vips_image_pio_input(VipsImage *image)
36463644
{
3647-
g_assert(vips_object_sanity(VIPS_OBJECT(image)));
3645+
if (!vips_object_sanity(VIPS_OBJECT(image)))
3646+
return -1;
36483647

36493648
#ifdef DEBUG_IO
36503649
printf("vips_image_pio_input: enabling partial input for %s\n",

libvips/iofuncs/object.c

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -470,23 +470,18 @@ vips_object_print_name(VipsObject *object)
470470
gboolean
471471
vips_object_sanity(VipsObject *object)
472472
{
473-
VipsObjectClass *class;
474-
char str[1000];
475-
VipsBuf buf = VIPS_BUF_STATIC(str);
476-
477473
if (!object) {
478-
printf("vips_object_sanity: null object\n");
479-
474+
vips_error("vips_object_sanity", _("null object"));
480475
return FALSE;
481476
}
482477

483-
class = VIPS_OBJECT_GET_CLASS(object);
478+
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(object);
479+
480+
char str[1000];
481+
VipsBuf buf = VIPS_BUF_STATIC(str);
484482
class->sanity(object, &buf);
485483
if (!vips_buf_is_empty(&buf)) {
486-
printf("sanity failure: ");
487-
vips_object_print_name(object);
488-
printf(" %s\n", vips_buf_all(&buf));
489-
484+
vips_error(class->nickname, "%s", vips_buf_all(&buf));
490485
return FALSE;
491486
}
492487

libvips/iofuncs/sink.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,8 @@ vips_sink_tile(VipsImage *im,
474474
Sink sink;
475475
int result;
476476

477-
g_assert(vips_object_sanity(VIPS_OBJECT(im)));
477+
if (!vips_object_sanity(VIPS_OBJECT(im)))
478+
return -1;
478479

479480
/* We don't use this, but make sure it's set in case any old binaries
480481
* are expecting it.

0 commit comments

Comments
 (0)