@@ -96,9 +96,10 @@ def test_http3_qpack_build_and_invocation_contract(self):
9696 # This probe verifies shell arguments; the real sanitizer runs are separate.
9797 import json
9898 import os
99+ import shlex
99100 with tempfile .TemporaryDirectory () as directory :
100101 location = pathlib .Path (directory )
101- compiler = location / 'compiler.py'
102+ compiler = location / 'compiler with spaces .py'
102103 compiler .write_text ('''#!/usr/bin/env python3
103104import json, os, pathlib, sys
104105if '--version' in sys.argv:
@@ -110,15 +111,15 @@ def test_http3_qpack_build_and_invocation_contract(self):
110111output.chmod(0o755)
111112''' )
112113 compiler .chmod (0o755 )
113- config = location / 'native'
114+ config = location / 'native config '
114115 config .mkdir ()
115116 (config / 'config.h' ).write_text ('/* TLS disabled */\n ' )
116- build = location / 'fuzz'
117+ build = location / 'fuzz output '
117118 environment = dict (os .environ , FUZZ_ARGUMENT_LOG = str (location / 'compile.jsonl' ),
118119 FUZZ_RUN_LOG = str (location / 'run.jsonl' ))
119120 result = subprocess .run ([
120121 'bash' , str (ROOT / 'scripts/run-v3-protocol-fuzz.sh' ),
121- '--build-dir' , str (build ), '--targets' , 'http3_engine qpack' ,
122+ '--build-dir' , str (build ), '--targets' , 'http3_engine qpack http1_head ' ,
122123 '--compiler' , str (compiler ), '--native-config-dir' , str (config ),
123124 '--runs' , '9' , '--seconds' , '2' ], env = environment ,
124125 capture_output = True , text = True , timeout = 15 )
@@ -128,8 +129,13 @@ def test_http3_qpack_build_and_invocation_contract(self):
128129 for target , macro , length in [('http3_engine' , '-DHTTP3_ENGINE_LIBFUZZER' , 4096 ),
129130 ('qpack' , '-DQPACK_LIBFUZZER' , 512 )]:
130131 arguments = next (args for args in builds if macro in args )
132+ self .assertNotIn ('' , arguments )
131133 self .assertIn ('-fsanitize=fuzzer,address,undefined' , arguments )
134+ self .assertIn ('-I' + str (config ), arguments )
132135 self .assertIn (str (ROOT / 'src/detail/http3_connection.cpp' ), arguments )
136+ self .assertEqual (arguments [- 2 :], ['-o' , str (build / target / 'fuzz' )])
137+ self .assertEqual (shlex .split ((build / target / 'compile.sh' ).read_text ()),
138+ [str (compiler )] + arguments )
133139 self .assertTrue ((build / target / 'sources.list' ).is_file ())
134140 invocation = next (args for args in runs if args [0 ] == str (build / target / 'fuzz' ))
135141 for flag in ('-runs=9' , '-max_total_time=2' , '-timeout=5' , '-rss_limit_mb=512' ,
@@ -138,6 +144,57 @@ def test_http3_qpack_build_and_invocation_contract(self):
138144 self .assertEqual (invocation [1 ], str (build / target / 'seeds' ))
139145 self .assertTrue (list ((build / target / 'seeds' ).glob ('*.seed' )))
140146 self .assertTrue ((build / target / 'reproduce.sh' ).is_file ())
147+ head = next (args for args in builds if str (ROOT / 'test/fuzz/http1_head_fuzz.cpp' ) in args )
148+ self .assertNotIn ('' , head )
149+ self .assertIn ('-fsanitize=fuzzer,address,undefined' , head )
150+ self .assertEqual (head [- 2 :], ['-o' , str (build / 'http1_head/fuzz' )])
151+ self .assertEqual (shlex .split ((build / 'http1_head/compile.sh' ).read_text ()),
152+ [str (compiler )] + head )
153+ self .assertEqual (len (builds ), 4 ) # Instrumentation probe and three selected targets.
154+
155+ def test_callback_provider_arguments_preserve_boundaries_and_link_order (self ):
156+ import json
157+ import os
158+ import shlex
159+ with tempfile .TemporaryDirectory () as directory :
160+ location = pathlib .Path (directory )
161+ compiler = location / 'compiler with spaces'
162+ compiler .write_text ('''#!/usr/bin/env python3
163+ import json, os, pathlib, sys
164+ if '--version' in sys.argv:
165+ print('test argument recorder'); sys.exit(0)
166+ with open(os.environ['FUZZ_ARGUMENT_LOG'], 'a') as log:
167+ log.write(json.dumps(sys.argv[1:]) + '\\ n')
168+ output = pathlib.Path(sys.argv[sys.argv.index('-o') + 1])
169+ output.write_text('#!/bin/sh\\ nexit 0\\ n')
170+ output.chmod(0o755)
171+ ''' )
172+ compiler .chmod (0o755 )
173+ config = location / 'native config'
174+ (config / 'src' ).mkdir (parents = True )
175+ (config / 'config.h' ).write_text ('#define NATIVE_V3_TLS 1\n ' )
176+ cflags = ['-I' + str (location / 'provider headers' ), '-DPROVIDER_VALUE=two words' ]
177+ libs = ['-L' + str (location / 'provider libs' ), '-lssl' , '-lcrypto' ]
178+ (config / 'src/Makefile' ).write_text ('V3_TLS_CFLAGS = ' + shlex .join (cflags )
179+ + '\n V3_TLS_LIBS = ' + shlex .join (libs ) + '\n ' )
180+ build = location / 'fuzz output'
181+ log = location / 'compile.jsonl'
182+ result = subprocess .run (['bash' , str (ROOT / 'scripts/run-v3-protocol-fuzz.sh' ),
183+ '--build-dir' , str (build ), '--targets' , 'quic_tls_callbacks' ,
184+ '--compiler' , str (compiler ), '--native-config-dir' , str (config ),
185+ '--runs' , '1' , '--seconds' , '1' ],
186+ env = dict (os .environ , FUZZ_ARGUMENT_LOG = str (log )),
187+ capture_output = True , text = True , timeout = 15 )
188+ self .assertEqual (result .returncode , 0 , result .stderr )
189+ arguments = json .loads (log .read_text ().splitlines ()[- 1 ])
190+ self .assertNotIn ('' , arguments )
191+ start = arguments .index ('-DQUIC_TLS_CALLBACKS_LIBFUZZER' )
192+ self .assertEqual (arguments [start :start + 5 ],
193+ ['-DQUIC_TLS_CALLBACKS_LIBFUZZER' , '-I' + str (ROOT / 'test' ), '-I' + str (config )] + cflags )
194+ self .assertEqual (arguments [- 5 :], libs + ['-o' , str (build / 'quic_tls_callbacks/fuzz' )])
195+ self .assertEqual (shlex .split ((build / 'quic_tls_callbacks/compile.sh' ).read_text ()),
196+ [str (compiler )] + arguments )
197+ self .assertEqual ((build / 'quic_tls_callbacks/provider.log' ).read_text ().splitlines (), cflags + libs )
141198
142199 def test_seed_and_corpus_arguments_reject_invalid_inputs_before_compilation (self ):
143200 for seed in ('0' , '-1' , '4294967296' , '$(touch marker)' , '12x' ):
0 commit comments