From fbd86a31ce8a25900ca897676be252655dbe7bc9 Mon Sep 17 00:00:00 2001 From: Toddr Bot Date: Wed, 8 Apr 2026 05:04:34 +0000 Subject: [PATCH] Validate context() restore inputs to prevent buffer overread The context() method, when called with arguments to restore internal state, reads exactly 16 bytes from the state buffer without checking its length. A short string causes a heap buffer overread. Similarly, the optional unprocessed-data argument should be at most 63 bytes (one partial block) but was passed through unchecked. Add croak() guards for both conditions and corresponding tests. Co-Authored-By: Claude Opus 4.6 --- MD5.xs | 4 ++++ t/context.t | 10 +++++++++- t/files.t | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/MD5.xs b/MD5.xs index 581b0e6..1c61c13 100644 --- a/MD5.xs +++ b/MD5.xs @@ -699,6 +699,8 @@ context(ctx, ...) STRLEN len; unsigned long blocks = SvUV(ST(1)); unsigned char *buf = (unsigned char *)(SvPV(ST(2), len)); + if (len < 16) + croak("Digest::MD5 context state must be exactly 16 bytes, got %"UVuf, (UV)len); ctx->A = buf[ 0] | (buf[ 1]<<8) | (buf[ 2]<<16) | (buf[ 3]<<24); ctx->B = buf[ 4] | (buf[ 5]<<8) | (buf[ 6]<<16) | (buf[ 7]<<24); ctx->C = buf[ 8] | (buf[ 9]<<8) | (buf[10]<<16) | (buf[11]<<24); @@ -707,6 +709,8 @@ context(ctx, ...) ctx->bytes_high = blocks >> 26; if (items == 4) { buf = (unsigned char *)(SvPV(ST(3), len)); + if (len > 63) + croak("Digest::MD5 context data must be at most 63 bytes, got %"UVuf, (UV)len); MD5Update(ctx, buf, len); } XSRETURN(1); /* ctx */ diff --git a/t/context.t b/t/context.t index a4d9782..b54ec15 100644 --- a/t/context.t +++ b/t/context.t @@ -2,7 +2,7 @@ use strict; use warnings; -use Test::More tests => 35; +use Test::More tests => 37; use Digest::MD5; foreach my $length ( @@ -41,3 +41,11 @@ foreach my $length ( is $got, $expect, "[$length] saved context"; } + +# Validate that context() croaks on short state buffer +eval { Digest::MD5->new->context(0, "short") }; +like $@, qr/must be exactly 16 bytes/, "context() croaks on short state buffer"; + +# Validate that context() croaks on overlong unprocessed data +eval { Digest::MD5->new->context(0, "\0" x 16, "x" x 64) }; +like $@, qr/must be at most 63 bytes/, "context() croaks on overlong data buffer"; diff --git a/t/files.t b/t/files.t index 2810c12..7361710 100644 --- a/t/files.t +++ b/t/files.t @@ -22,7 +22,7 @@ EOT # This is the output of: 'md5sum README MD5.xs rfc1321.txt' $EXPECT = <