runtests: support memory-limits per test

The idea here is to set limits per test how many allocations and maximum
amount of memory it is allowed to use. This is a means to make sure the
number and total size of allocations are kept in check and don't
mistakenly "blow up".

If runtests.pl detects that the given limits have been exceeded it fails
the test case with an error.

The `<verify>` part now supports `<limits>`, and in this section two
limits can be set for each test (verified in debug builds only):

    Allocations: [number of allocation calls]
    Maximum allocated: [maximum concurrent memory allocated]

Default limits (used if nothing is set in the test file):

    Allocations: 1000
    Maximum allocated: 1000000

Closes #17821
This commit is contained in:
Daniel Stenberg 2025-07-04 23:57:03 +02:00
parent 2db8ae480f
commit f00a2add71
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
22 changed files with 120 additions and 1 deletions

View file

@ -49,5 +49,9 @@ User-Agent: curl/%VERSION
Accept: */*
</protocol>
<limits>
Allocations: 135
Maximum allocated: 135415
</limits>
</verify>
</testcase>

View file

@ -92,5 +92,8 @@ Content-Type: application/x-www-form-urlencoded
Expect: 100-continue
</protocol>
<limits>
Maximum allocated: 3200000
</limits>
</verify>
</testcase>

View file

@ -67,5 +67,8 @@ http://%HOSTIP:%HTTPPORT/want/%TESTNUMBER
<stdout>
0
</stdout>
<limits>
Allocations: 1300
</limits>
</verify>
</testcase>

View file

@ -38,5 +38,8 @@ XXXXXXXx
<errorcode>
1
</errorcode>
<limits>
Allocations: 3100
</limits>
</verify>
</testcase>

View file

@ -40,6 +40,9 @@ CURLUPART_URL 10000000 bytes URL == 3 (Malformed input to a URL function)
CURLUPART_SCHEME 10000000 bytes scheme == 3 (Malformed input to a URL function)
CURLUPART_USER 10000000 bytes user == 3 (Malformed input to a URL function)
</stdout>
<limits>
Maximum allocated: 10010000
</limits>
</verify>
</testcase>

View file

@ -38,5 +38,8 @@ lib%TESTNUMBER
<stdout>
success
</stdout>
<limits>
Allocations: 3000
</limits>
</verify>
</testcase>

View file

@ -18,5 +18,12 @@ unittest
<name>
x509 parsing
</name>
<verify>
<limits>
Allocations: 14000
</limits>
</verify>
</client>
</testcase>

View file

@ -66,5 +66,8 @@ Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ==
68 65 6c 6c 6f
RECFLAGS: 1
</stdout>
<limits>
Maximum allocated: 1300000
</limits>
</verify>
</testcase>

View file

@ -55,5 +55,8 @@ Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ==
<errorcode>
22
</errorcode>
<limits>
Maximum allocated: 1300000
</limits>
</verify>
</testcase>

View file

@ -19,4 +19,9 @@ unittest
bufq unit tests
</name>
</client>
<verify>
<limits>
Allocations: 6300
</limits>
</verify>
</testcase>

View file

@ -171,5 +171,8 @@ https://localhost:%HTTPSPORT/%TESTNUMBER %CERTDIR/certs/test-ca.crt
# Verify data after the test has been "shot"
<verify>
<limits>
Allocations: 13300
</limits>
</verify>
</testcase>

View file

@ -73,5 +73,8 @@ https://this.hsts.example./%TESTNUMBER
<errorcode>
56
</errorcode>
<limits>
Allocations: 1100
</limits>
</verify>
</testcase>

View file

@ -209,5 +209,8 @@ Accept: */*
Cookie: name150=could-be-large-150; name149=could-be-large-149; name148=could-be-large-148; name147=could-be-large-147; name146=could-be-large-146; name145=could-be-large-145; name144=could-be-large-144; name143=could-be-large-143; name142=could-be-large-142; name141=could-be-large-141; name140=could-be-large-140; name139=could-be-large-139; name138=could-be-large-138; name137=could-be-large-137; name136=could-be-large-136; name135=could-be-large-135; name134=could-be-large-134; name133=could-be-large-133; name132=could-be-large-132; name131=could-be-large-131; name130=could-be-large-130; name129=could-be-large-129; name128=could-be-large-128; name127=could-be-large-127; name126=could-be-large-126; name125=could-be-large-125; name124=could-be-large-124; name123=could-be-large-123; name122=could-be-large-122; name121=could-be-large-121; name120=could-be-large-120; name119=could-be-large-119; name118=could-be-large-118; name117=could-be-large-117; name116=could-be-large-116; name115=could-be-large-115; name114=could-be-large-114; name113=could-be-large-113; name112=could-be-large-112; name111=could-be-large-111; name110=could-be-large-110; name109=could-be-large-109; name108=could-be-large-108; name107=could-be-large-107; name106=could-be-large-106; name105=could-be-large-105; name104=could-be-large-104; name103=could-be-large-103; name102=could-be-large-102; name101=could-be-large-101; name100=could-be-large-100; name99=could-be-large-99; name98=could-be-large-98; name97=could-be-large-97; name96=could-be-large-96; name95=could-be-large-95; name94=could-be-large-94; name93=could-be-large-93; name92=could-be-large-92; name91=could-be-large-91; name90=could-be-large-90; name89=could-be-large-89; name88=could-be-large-88; name87=could-be-large-87; name86=could-be-large-86; name85=could-be-large-85; name84=could-be-large-84; name83=could-be-large-83; name82=could-be-large-82; name81=could-be-large-81; name80=could-be-large-80; name79=could-be-large-79; name78=could-be-large-78; name77=could-be-large-77; name76=could-be-large-76; name75=could-be-large-75; name74=could-be-large-74; name73=could-be-large-73; name72=could-be-large-72; name71=could-be-large-71; name70=could-be-large-70; name69=could-be-large-69; name68=could-be-large-68; name67=could-be-large-67; name66=could-be-large-66; name65=could-be-large-65; name64=could-be-large-64; name63=could-be-large-63; name62=could-be-large-62; name61=could-be-large-61; name60=could-be-large-60; name59=could-be-large-59; name58=could-be-large-58; name57=could-be-large-57; name56=could-be-large-56; name55=could-be-large-55; name54=could-be-large-54; name53=could-be-large-53; name52=could-be-large-52; name51=could-be-large-51; name50=could-be-large-50; name49=could-be-large-49; name48=could-be-large-48; name47=could-be-large-47; name46=could-be-large-46; name45=could-be-large-45; name44=could-be-large-44; name43=could-be-large-43; name42=could-be-large-42; name41=could-be-large-41; name40=could-be-large-40; name39=could-be-large-39; name38=could-be-large-38; name37=could-be-large-37; name36=could-be-large-36; name35=could-be-large-35; name34=could-be-large-34; name33=could-be-large-33; name32=could-be-large-32; name31=could-be-large-31; name30=could-be-large-30; name29=could-be-large-29; name28=could-be-large-28; name27=could-be-large-27; name26=could-be-large-26; name25=could-be-large-25; name24=could-be-large-24; name23=could-be-large-23; name22=could-be-large-22; name21=could-be-large-21; name20=could-be-large-20; name19=could-be-large-19; name18=could-be-large-18; name17=could-be-large-17; name16=could-be-large-16; name15=could-be-large-15; name14=could-be-large-14; name13=could-be-large-13; name12=could-be-large-12; name11=could-be-large-11; name10=could-be-large-10; name9=could-be-large-9; name8=could-be-large-8; name7=could-be-large-7; name6=could-be-large-6; name5=could-be-large-5; name4=could-be-large-4; name3=could-be-large-3; name2=could-be-large-2; name1=could-be-large-1
</protocol>
<limits>
Allocations: 1100
</limits>
</verify>
</testcase>

View file

@ -57,5 +57,8 @@ Refuse tunneling protocols through HTTP proxy
<errorcode>
56
</errorcode>
<limits>
Allocations: 1700
</limits>
</verify>
</testcase>

View file

@ -52,5 +52,8 @@ Accept: */*
<errorcode>
56
</errorcode>
<limits>
Allocations: 1900
</limits>
</verify>
</testcase>

View file

@ -54,5 +54,9 @@ Host: %HOSTIP:%HTTPPORT
Accept: */*
</protocol>
<limits>
Allocations: 80
Maximum allocated: 33400
</limits>
</verify>
</testcase>

View file

@ -120,5 +120,8 @@ This file should have permissions 777
This is content of file "file.txt"
Some junk ;-) This file does not really exist.
</stdout>
<limits>
Allocations: 1500
</limits>
</verify>
</testcase>

View file

@ -52,5 +52,8 @@ Accept: */*
<errorcode>
100
</errorcode>
<limits>
Allocations: 6000
</limits>
</verify>
</testcase>

View file

@ -27,7 +27,11 @@ multi - add many easy handles
</file>
</client>
# Verify data after the test has been "shot"
# 1000 easy handles needs memory
<verify>
<limits>
Maximum allocated: 6000000
Allocations: 7000
</limits>
</verify>
</testcase>

View file

@ -38,5 +38,8 @@ PASS secret
LIST
QUIT
</protocol>
<limits>
Allocations: 2200
</limits>
</verify>
</testcase>

View file

@ -1698,6 +1698,48 @@ sub singletest_check {
else {
$ok .= "m";
}
my @more=`$memanalyze -v "$logdir/$MEMDUMP"`;
my $allocs;
my $max;
for(@more) {
if(/^Allocations: (\d+)/) {
$allocs = $1;
}
elsif(/^Maximum allocated: (\d+)/) {
$max = $1;
}
}
my @limits = getpart("verify", "limits");
my $lim_allocs = 1000; # high default values
my $lim_max = 1000000;
for(@limits) {
if(/^Allocations: (\d+)/i) {
$lim_allocs = $1;
}
elsif(/^Maximum allocated: (\d+)/i) {
$lim_max = $1;
}
}
logmsg "did $allocs allocations, $lim_allocs allowed\n"
if($verbose);
logmsg "allocated $max maximum, $lim_max allowed\n"
if($verbose);
if($allocs > $lim_allocs) {
logmsg "\n** TOO MANY ALLOCS\n";
logmsg "$lim_allocs allocations allowed, did $allocs\n";
# timestamp test result verification end
$timevrfyend{$testnum} = Time::HiRes::time();
return -1;
}
if($max > $lim_max) {
logmsg "\n** TOO MUCH TOTAL ALLOCATION\n";
logmsg "$lim_max maximum allocation allowed, did $max\n";
# timestamp test result verification end
$timevrfyend{$testnum} = Time::HiRes::time();
return -1;
}
}
}
else {