Commit 175af0b9 authored by Dridi Boukelmoune's avatar Dridi Boukelmoune

Big bang rewrite of m00049.vtc

Now that vtc.typesize() is a bit more reliable, it's easier to deal with
architecture differences for the sizeof struct vrt_blob. For a much more
reliable failure check, it now looks at logs.

There may also have been a time when depending on the error we trigger
we could fail with a 500 or a 503, this is no longer the case.

For the nitty-gritty of the new test case, there are some comments to
hopefully help (at least my future self) decipher this beast.

This new test case remains stable even with the changes from #3123.
parent 27bed0ed
varnishtest "VMOD blob workspace overflow conditions" varnishtest "VMOD blob workspace overflow conditions"
varnish v1 -vcl { # This test case is tricky to get right and is somewhat repetitive. The VCL
# below provides off-the-shelves subs to abstract away some of the complexity.
# Since not all of them may be called by all VCLs we also need to ensure this
# will not result in a compilation failure.
varnish v1 -cliok "param.set vcc_err_unref off"
shell {
cat >vrt_blob.vcl <<-EOF
import blob; import blob;
import std;
import vtc; import vtc;
backend b None;
backend be none;
sub vcl_recv { sub vcl_recv {
if (req.url == "/1") { set req.http.vrt_blob = vtc.typesize("uzp");
# Too small for the vmod_priv object.
vtc.workspace_alloc(client, -8);
}
elsif (req.url == "/2") {
# Likely large enough for the vmod_priv object,
# but not enough left over for the decode.
vtc.workspace_alloc(client, -33);
}
elsif (req.url == "/3") {
# Enough for the decode, but not enough left
# over for the encode.
vtc.workspace_alloc(client, -50);
}
set req.http.Decode
= blob.encode(blob=blob.decode(encoded="1234567890"));
} }
sub vcl_miss { sub shrink {
if (req.url == "/4") { std.log("shrink");
# Enough for the req.hash BLOB (vmod_priv + 32 vtc.workspace_alloc(client, -std.integer(req.http.leave));
# bytes), but not enough left over for the
# encoding.
vtc.workspace_alloc(client, -65);
set req.http.Encode = blob.encode(blob=req.hash);
}
return( synth(200) );
} }
} -start
client c1 { sub leave_struct {
txreq -url "/1" set req.http.leave = std.integer(req.http.vrt_blob);
call shrink;
}
sub leave_half_struct {
set req.http.leave = std.integer(req.http.vrt_blob) / 2;
call shrink;
}
sub leave_blob {
set req.http.leave = std.integer(req.http.vrt_blob) +
std.integer(req.http.blob);
call shrink;
}
EOF
}
# For each type of BLOB codec we may use some of the prepared clients. It is
# important not to run them concurently to ensure we can predictably peek at
# logs.
client c-struct {
txreq -url "/struct"
rxresp rxresp
expect resp.status >= 500 expect resp.status == 503
expect resp.status <= 503 expect resp.reason == "VCL failed"
expect resp.reason ~ {(?i)^VCL Failed$|^Internal Server Error$} expect_close
} -run }
client c2 { client c-encode {
txreq -url "/2" txreq -url "/encode"
rxresp rxresp
expect resp.status >= 500 expect resp.status == 503
expect resp.status <= 503 expect resp.reason == "VCL failed"
expect resp.reason ~ {(?i)^VCL Failed$|^Internal Server Error$} expect_close
} -run }
client c3 { client c-decode {
txreq -url "/3" txreq -url "/decode"
rxresp rxresp
expect resp.status >= 500 expect resp.status == 503
expect resp.status <= 503 expect resp.reason == "VCL failed"
expect resp.reason ~ {(?i)^VCL Failed$|^Internal Server Error$} expect_close
} -run }
client c4 { client c-req-hash {
txreq -url "/4" txreq -url "/req.hash"
rxresp rxresp
expect resp.status >= 500 expect resp.status == 503
expect resp.status <= 503 expect resp.reason == "VCL failed"
expect resp.reason ~ {(?i)^VCL Failed$|^Internal Server Error$} expect_close
} -run }
client c-sub {
txreq -url "/sub"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
}
# For each type of BLOB codec we will check failure conditions from the logs
# so we provision a log reader that will only deal with meaningful log records
# for this job. This means that all logexpect expect commands must use 0 as
# their first argument. Meaning we make a comprehensive check of all the log
# records that aren't filtered out.
logexpect l1 -v v1 -i ReqURL,VCL_Error,VCL_Log,VCL_use
# IDENTITY codec
# Similar tests with BASE64 encoding
varnish v1 -vcl { varnish v1 -vcl {
import blob; include "${tmpdir}/vrt_blob.vcl";
import vtc;
backend b None;
sub vcl_recv { sub vcl_recv {
if (req.url == "/1") { if (req.url ~ "decode") {
vtc.workspace_alloc(client, -33); # Not enough space to collect the string.
set req.http.leave = 5;
call shrink;
}
if (req.url ~ "struct") {
# Enough space to collect the decoded string.
# Not enough space to allocate a blob (aligned).
set req.http.leave = 16;
call shrink;
} }
elsif (req.url == "/2") { if (req.url ~ "encode") {
vtc.workspace_alloc(client, -50); # Enough space to decode the string.
# Not enough space to encode the blob.
set req.http.blob = 16;
call leave_blob;
} }
set req.http.Decode blob.encode(blob=blob.decode(encoded="1234567890"));
= blob.encode(blob=blob.decode(BASE64, return (synth(200));
encoded="MTIzNDU2Nzg5MA=="));
} }
} -start
sub vcl_miss { logexpect l1 {
if (req.url == "/3") { expect 0 * VCL_use vcl1
vtc.workspace_alloc(client, -65); expect 0 = ReqURL decode
set req.http.Encode expect 0 = VCL_Log shrink
= blob.encode(BASE64, blob=req.hash); expect 0 = VCL_Error "cannot decode, out of space"
expect 0 * VCL_use vcl1
expect 0 = ReqURL struct
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "Workspace overflow .blob.decode."
expect 0 * VCL_use vcl1
expect 0 = ReqURL encode
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "cannot encode, out of space"
} -start
client c-decode -run
client c-struct -run
client c-encode -run
logexpect l1 -wait
# BASE64 codec
varnish v1 -vcl {
include "${tmpdir}/vrt_blob.vcl";
sub vcl_recv {
if (req.url ~ "decode") {
# Not enough space to collect the string.
set req.http.leave = 5;
call shrink;
}
if (req.url ~ "struct") {
# Enough space to collect the decoded string.
# Not enough space to allocate a blob.
set req.http.leave = 16;
call shrink;
} }
return( synth(200) ); if (req.url ~ "encode") {
# Enough space to decode the string.
# Not enough space to encode the blob.
set req.http.blob = 16;
call leave_blob;
}
blob.encode(
blob=blob.decode(BASE64, encoded="MTIzNDU2Nzg5MA=="));
return (synth(200));
} }
} }
client c1 -run logexpect l1 {
client c2 -run expect 0 * VCL_use vcl2
client c3 -run expect 0 = ReqURL decode
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "cannot decode, out of space"
expect 0 * VCL_use vcl2
expect 0 = ReqURL struct
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "Workspace overflow .blob.decode."
expect 0 * VCL_use vcl2
expect 0 = ReqURL encode
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "cannot encode, out of space"
} -start
client c-decode -run
client c-struct -run
client c-encode -run
logexpect l1 -wait
# URL codec
# URL encoding
varnish v1 -vcl { varnish v1 -vcl {
import blob; include "${tmpdir}/vrt_blob.vcl";
import vtc;
backend b None;
sub vcl_recv { sub vcl_recv {
if (req.url == "/1") { if (req.url ~ "decode") {
vtc.workspace_alloc(client, -33); # Not enough space to collect the string.
set req.http.leave = 5;
call shrink;
} }
elsif (req.url == "/2") { if (req.url ~ "struct") {
vtc.workspace_alloc(client, -50); # Enough space to collect the decoded string.
# Not enough space to allocate a blob (aligned).
set req.http.leave = 16;
call shrink;
} }
set req.http.Decode if (req.url ~ "encode") {
= blob.encode(blob=blob.decode(URL, # Enough space to decode the string.
encoded="1234567890")); # Not enough space to encode the blob.
} set req.http.blob = 16;
call leave_blob;
sub vcl_miss {
if (req.url == "/3") {
vtc.workspace_alloc(client, -65);
set req.http.Encode = blob.encode(URL, blob=req.hash);
} }
return( synth(200) ); blob.encode(blob=blob.decode(URL, encoded="1234567890"));
return (synth(200));
} }
} }
client c1 -run logexpect l1 {
client c2 -run expect 0 * VCL_use vcl3
client c3 -run expect 0 = ReqURL decode
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "cannot decode, out of space"
expect 0 * VCL_use vcl3
expect 0 = ReqURL struct
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "Workspace overflow .blob.decode."
expect 0 * VCL_use vcl3
expect 0 = ReqURL encode
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "cannot encode, out of space"
} -start
client c-decode -run
client c-struct -run
client c-encode -run
logexpect l1 -wait
# HEX codec
# HEX encoding
varnish v1 -vcl { varnish v1 -vcl {
import blob; include "${tmpdir}/vrt_blob.vcl";
import vtc;
backend b None;
sub vcl_recv { sub vcl_recv {
if (req.url == "/1") { if (req.url ~ "decode") {
vtc.workspace_alloc(client, -33); # Not enough space to collect the string.
set req.http.leave = 5;
call shrink;
} }
elsif (req.url == "/2") { if (req.url ~ "struct") {
vtc.workspace_alloc(client, -50); # Enough space to collect the decoded string.
# Not enough space to allocate a blob (aligned).
set req.http.leave = 20;
call shrink;
} }
set req.http.Decode if (req.url ~ "encode") {
= blob.encode(blob=blob.decode(HEX, # Enough space to decode the string.
encoded="31323334353637383930")); # Not enough space to encode the blob.
} set req.http.blob = 20;
call leave_blob;
sub vcl_miss {
if (req.url == "/3") {
vtc.workspace_alloc(client, -65);
set req.http.Encode = blob.encode(HEX, blob=req.hash);
} }
return( synth(200) ); blob.encode(
blob=blob.decode(HEX, encoded="31323334353637383930"));
return (synth(200));
} }
} }
client c1 -run logexpect l1 {
client c2 -run expect 0 * VCL_use vcl4
client c3 -run expect 0 = ReqURL decode
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "cannot decode, out of space"
expect 0 * VCL_use vcl4
expect 0 = ReqURL struct
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "Workspace overflow .blob.decode."
expect 0 * VCL_use vcl4
expect 0 = ReqURL encode
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "cannot encode, out of space"
} -start
client c-decode -run
client c-struct -run
client c-encode -run
logexpect l1 -wait
# blob.sub() function
# sub() function
varnish v1 -vcl { varnish v1 -vcl {
import blob; include "${tmpdir}/vrt_blob.vcl";
import vtc;
backend b None;
sub vcl_miss { sub vcl_miss {
if (req.url == "/1") { if (req.url ~ "req.hash") {
# Not enough for req.hash + vmod_priv # Not enough to create the req.hash blob.
vtc.workspace_alloc(client, -65); call leave_half_struct;
} }
elsif (req.url == "/2") { if (req.url ~ "struct") {
# Not enough for req.hash + vmod_priv + 30 bytes # Enough for the req.hash blob.
vtc.workspace_alloc(client, -90); # Not enough for the sub-blob.
call leave_struct;
} }
set req.http.Encode = blob.encode(blob=blob.sub(req.hash, 30B)); blob.encode(blob=blob.sub(req.hash, 30B));
return( synth(200) ); return (synth(200));
} }
} }
client c1 -run logexpect l1 {
client c2 -run expect 0 * VCL_use vcl5
expect 0 = ReqURL req.hash
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "Workspace overflow .req.hash."
expect 0 * VCL_use vcl5
expect 0 = ReqURL struct
expect 0 = VCL_Log shrink
expect 0 = VCL_Error "Workspace overflow .blob.sub."
} -start
client c-req-hash -run
client c-struct -run
logexpect l1 -wait
...@@ -534,6 +534,6 @@ vmod_sub(VRT_CTX, VCL_BLOB b, VCL_BYTES n, VCL_BYTES off) ...@@ -534,6 +534,6 @@ vmod_sub(VRT_CTX, VCL_BLOB b, VCL_BYTES n, VCL_BYTES off)
return null_blob; return null_blob;
return (VRT_blob(ctx, "blob.sub()", return (VRT_blob(ctx, "blob.sub",
(const char *)b->blob + off, n, b->type)); (const char *)b->blob + off, n, b->type));
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment