Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 37 additions & 23 deletions .github/workflows/testsuite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: perl -V
- name: install dependencies
uses: perl-actions/install-with-cpm@v1
uses: perl-actions/install-with-cpm@stable
with:
cpanfile: "cpanfile"
- name: Makefile.PL
Expand All @@ -35,9 +35,7 @@ jobs:
needs: [ubuntu]
env:
PERL_USE_UNSAFE_INC: 0
AUTHOR_TESTING: 1
AUTOMATED_TESTING: 1
RELEASE_TESTING: 1

runs-on: ubuntu-latest

Expand All @@ -46,6 +44,10 @@ jobs:
matrix:
perl-version:
[
"5.40",
"5.38",
"5.36",
"5.34",
"5.32",
"5.30",
"5.28",
Expand All @@ -58,48 +60,60 @@ jobs:
"5.14",
"5.12",
"5.10",
"5.8",
]

container:
image: perl:${{ matrix.perl-version }}

steps:
- uses: actions/checkout@v2
- run: perl -V
- name: install dependencies
uses: perl-actions/install-with-cpm@v1
- uses: actions/checkout@v4
- name: Set up perl ${{ matrix.perl-version }}
uses: shogo82148/actions-setup-perl@v1
with:
sudo: false
cpanfile: "cpanfile"
perl-version: ${{ matrix.perl-version }}
- run: perl -V
- name: Makefile.PL
run: perl -I$(pwd) Makefile.PL
- name: make
run: make
- name: make test
run: make test

macOS:
needs: [ubuntu]
env:
PERL_USE_UNSAFE_INC: 0
AUTHOR_TESTING: 1
AUTOMATED_TESTING: 1
RELEASE_TESTING: 1

runs-on: macOS-latest

strategy:
fail-fast: false
matrix:
perl-version: [latest]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: perl -V
- name: install dependencies
uses: perl-actions/install-with-cpm@v1
uses: perl-actions/install-with-cpm@stable
with:
cpanfile: "cpanfile"
- name: Makefile.PL
run: perl -I$(pwd) Makefile.PL
- name: make test
run: make test

windows:
needs: [ubuntu]
env:
PERL_USE_UNSAFE_INC: 0
AUTOMATED_TESTING: 1

runs-on: windows-latest

steps:
- uses: actions/checkout@v4
- name: Set up Strawberry Perl
uses: shogo82148/actions-setup-perl@v1
with:
perl-version: "5.38"
- run: perl -V
- name: Makefile.PL
run: perl Makefile.PL
- name: make
run: gmake
- name: make test
run: gmake test
13 changes: 10 additions & 3 deletions MD5.xs
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,16 @@ MD5Update(MD5_CTX* ctx, const U8* buf, STRLEN len)
buf, len);
#endif

ctx->bytes_low += len;
if (ctx->bytes_low < len) /* wrap around */
ctx->bytes_high++;
{
U32 old_bytes_low = ctx->bytes_low;
ctx->bytes_low += (U32)len;
TRUNC32(ctx->bytes_low);
if (ctx->bytes_low < old_bytes_low) /* carry from low add */
ctx->bytes_high++;
/* If STRLEN is wider than 32 bits, add the high bits of len */
if (sizeof(STRLEN) > 4)
ctx->bytes_high += (U32)((STRLEN)len >> 32);
}

if (fill) {
STRLEN missing = 64 - fill;
Expand Down
2 changes: 1 addition & 1 deletion t/files.t
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ EOT
# This is the output of: 'md5sum README MD5.xs rfc1321.txt'
$EXPECT = <<EOT;
2f93400875dbb56f36691d5f69f3eba5 README
f4b5da4e0f19b4c0ab374b7085ed8955 MD5.xs
c63d445f80edbcb725b6aa769039f775 MD5.xs
754b9db19f79dbc4992f7166eb0f37ce rfc1321.txt
EOT
}
Expand Down
83 changes: 83 additions & 0 deletions t/large.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/perl
# Test that byte counter handles values >= 2^32 correctly.
# Regression test for GitHub issue #6 / rt.cpan.org #123185
# where MD5Update's 32-bit byte counter overflowed for inputs >= 8 GiB.

use strict;
use warnings;
use Test::More;
use Config;

# This test only makes sense on 64-bit systems where STRLEN > 32 bits
if ($Config{ptrsize} < 8) {
plan skip_all => '64-bit system required';
}

plan tests => 3;

use Digest::MD5;

# Test 1: Verify byte counter tracks correctly across the 4 GiB boundary.
# Set context to just below 4 GiB, add data that crosses, check block count.
{
my $ctx = Digest::MD5->new;
my ($b0, $s0) = $ctx->context;

# Set to (2^26 - 1) blocks = 4 GiB - 64 bytes
my $near_boundary = (1 << 26) - 1;
$ctx->context($near_boundary, $s0);

# Add 128 bytes (2 blocks) to cross the 4 GiB boundary
$ctx->add("\x00" x 128);

my ($blocks_after) = $ctx->context;
is($blocks_after, $near_boundary + 2,
'block count correct after crossing 4 GiB boundary');
}

# Test 2: Verify context round-trips above 4 GiB.
{
my $ctx = Digest::MD5->new;
my ($b0, $s0) = $ctx->context;

# Set to 2^27 blocks = 8 GiB
my $target_blocks = 1 << 27;
$ctx->context($target_blocks, $s0);
my ($b2) = $ctx->context;

is($b2, $target_blocks,
'context round-trips block count above 4 GiB');
}

# Test 3: Verify that the digest is correct when byte counter crosses 4 GiB.
# We feed the same initial state and same data, but at two different
# "virtual" byte offsets that should produce the same final block count
# and same data path — one set via context, one built incrementally from
# that context with identical data.
{
my $ctx = Digest::MD5->new;
my ($b0, $s0) = $ctx->context;

# Set near 4 GiB boundary and add 128 bytes
my $near = (1 << 26) - 1;
my $ctx1 = Digest::MD5->new;
$ctx1->context($near, $s0);
$ctx1->add("\xAB" x 64);
# Capture state after first 64 bytes
my ($b1, $s1, $buf1) = $ctx1->context;
$ctx1->add("\xAB" x 64);
my $digest1 = $ctx1->hexdigest;

# Now recreate using context from midpoint
my $ctx2 = Digest::MD5->new;
if (defined $buf1) {
$ctx2->context($b1, $s1, $buf1);
} else {
$ctx2->context($b1, $s1);
}
$ctx2->add("\xAB" x 64);
my $digest2 = $ctx2->hexdigest;

is($digest1, $digest2,
'digest matches when state is saved/restored across 4 GiB boundary');
}
Loading