@@ -44,14 +44,15 @@ func runFormParsingLimitAnalysis(pass *analysis.Pass) (any, error) {
4444 }
4545
4646 issuesByPos := make (map [token.Pos ]* issue.Issue )
47+ handlerProtection := computeFormParsingHandlerProtection (ssaResult .SSA .SrcFuncs )
4748
4849 for _ , fn := range collectAnalyzerFunctions (ssaResult .SSA .SrcFuncs ) {
4950 requestParam , writerParam := findHandlerRequestAndWriterParams (fn )
5051 if requestParam == nil || writerParam == nil {
5152 continue
5253 }
5354
54- hasRequestBodyLimit := functionHasRequestBodyLimit ( fn , requestParam , writerParam )
55+ hasRequestBodyLimit := handlerProtection [ fn ]
5556 if hasRequestBodyLimit {
5657 continue
5758 }
@@ -82,6 +83,203 @@ func runFormParsingLimitAnalysis(pass *analysis.Pass) (any, error) {
8283 return issues , nil
8384}
8485
86+ func computeFormParsingHandlerProtection (srcFuncs []* ssa.Function ) map [* ssa.Function ]bool {
87+ protection := make (map [* ssa.Function ]bool )
88+ allFuncs := collectAnalyzerFunctions (srcFuncs )
89+ for _ , fn := range allFuncs {
90+ requestParam , writerParam := findHandlerRequestAndWriterParams (fn )
91+ if requestParam == nil || writerParam == nil {
92+ continue
93+ }
94+ if functionHasRequestBodyLimit (fn , requestParam , writerParam ) {
95+ protection [fn ] = true
96+ continue
97+ }
98+ if isProtectedByWrapperCall (fn , allFuncs ) {
99+ protection [fn ] = true
100+ }
101+ }
102+
103+ return protection
104+ }
105+
106+ func isProtectedByWrapperCall (handler * ssa.Function , allFuncs []* ssa.Function ) bool {
107+ for _ , fn := range allFuncs {
108+ if fn == nil {
109+ continue
110+ }
111+ for _ , block := range fn .Blocks {
112+ for _ , instr := range block .Instrs {
113+ callInstr , ok := instr .(ssa.CallInstruction )
114+ if ! ok {
115+ continue
116+ }
117+ common := callInstr .Common ()
118+ if common == nil {
119+ continue
120+ }
121+ wrapper := common .StaticCallee ()
122+ if wrapper == nil {
123+ continue
124+ }
125+
126+ for argIndex , arg := range common .Args {
127+ if ! valueDependsOn (arg , handler , 0 ) {
128+ continue
129+ }
130+ if wrapperProtectsParamHandler (wrapper , argIndex ) {
131+ return true
132+ }
133+ }
134+ }
135+ }
136+ }
137+
138+ return false
139+ }
140+
141+ func wrapperProtectsParamHandler (wrapper * ssa.Function , paramIndex int ) bool {
142+ if wrapper == nil || paramIndex < 0 || paramIndex >= len (wrapper .Params ) {
143+ return false
144+ }
145+ handlerParam := wrapper .Params [paramIndex ]
146+
147+ if wrapperDelegatesWithRequestLimit (wrapper , handlerParam ) {
148+ return true
149+ }
150+
151+ for _ , block := range wrapper .Blocks {
152+ for _ , instr := range block .Instrs {
153+ makeClosure , ok := instr .(* ssa.MakeClosure )
154+ if ! ok {
155+ continue
156+ }
157+ closureFn , ok := makeClosure .Fn .(* ssa.Function )
158+ if ! ok || closureFn == nil {
159+ continue
160+ }
161+
162+ requestParam , writerParam := findHandlerRequestAndWriterParams (closureFn )
163+ if requestParam == nil || writerParam == nil {
164+ continue
165+ }
166+ if ! functionHasRequestBodyLimit (closureFn , requestParam , writerParam ) {
167+ continue
168+ }
169+
170+ for bindingIndex , binding := range makeClosure .Bindings {
171+ if ! bindingDependsOnValue (binding , handlerParam ) {
172+ continue
173+ }
174+ if closureDelegatesWithRequestLimit (closureFn , bindingIndex , requestParam , writerParam ) {
175+ return true
176+ }
177+ }
178+ }
179+ }
180+
181+ return false
182+ }
183+
184+ func bindingDependsOnValue (binding ssa.Value , target ssa.Value ) bool {
185+ if valueDependsOn (binding , target , 0 ) {
186+ return true
187+ }
188+
189+ alloc , ok := binding .(* ssa.Alloc )
190+ if ! ok {
191+ return false
192+ }
193+
194+ for _ , ref := range safeReferrers (alloc ) {
195+ store , ok := ref .(* ssa.Store )
196+ if ! ok {
197+ continue
198+ }
199+ if store .Addr != alloc {
200+ continue
201+ }
202+ if valueDependsOn (store .Val , target , 0 ) {
203+ return true
204+ }
205+ }
206+
207+ return false
208+ }
209+
210+ func wrapperDelegatesWithRequestLimit (wrapper * ssa.Function , handlerValue ssa.Value ) bool {
211+ requestParam , writerParam := findHandlerRequestAndWriterParams (wrapper )
212+ if requestParam == nil || writerParam == nil {
213+ return false
214+ }
215+ if ! functionHasRequestBodyLimit (wrapper , requestParam , writerParam ) {
216+ return false
217+ }
218+ return hasServeHTTPDelegation (wrapper , handlerValue , writerParam , requestParam )
219+ }
220+
221+ func closureDelegatesWithRequestLimit (closure * ssa.Function , freeVarIndex int , requestParam * ssa.Parameter , writerParam * ssa.Parameter ) bool {
222+ if freeVarIndex < 0 || freeVarIndex >= len (closure .FreeVars ) {
223+ return false
224+ }
225+ handlerValue := closure .FreeVars [freeVarIndex ]
226+ return hasServeHTTPDelegation (closure , handlerValue , writerParam , requestParam )
227+ }
228+
229+ func hasServeHTTPDelegation (fn * ssa.Function , handlerValue ssa.Value , writerValue ssa.Value , requestValue ssa.Value ) bool {
230+ for _ , block := range fn .Blocks {
231+ for _ , instr := range block .Instrs {
232+ call , ok := instr .(* ssa.Call )
233+ if ! ok {
234+ continue
235+ }
236+ common := call .Common ()
237+ if common == nil {
238+ continue
239+ }
240+
241+ var (
242+ receiver ssa.Value
243+ writer ssa.Value
244+ request ssa.Value
245+ )
246+
247+ if method := common .Method ; method != nil && method .Name () == "ServeHTTP" {
248+ if len (common .Args ) < 2 {
249+ continue
250+ }
251+ receiver = common .Value
252+ writer = common .Args [0 ]
253+ request = common .Args [1 ]
254+ } else {
255+ callee := common .StaticCallee ()
256+ if callee == nil || callee .Name () != "ServeHTTP" || callee .Signature == nil || callee .Signature .Recv () == nil {
257+ continue
258+ }
259+ if len (common .Args ) < 3 {
260+ continue
261+ }
262+ receiver = common .Args [0 ]
263+ writer = common .Args [1 ]
264+ request = common .Args [2 ]
265+ }
266+
267+ if ! valueDependsOn (receiver , handlerValue , 0 ) {
268+ continue
269+ }
270+ if ! valueDependsOn (writer , writerValue , 0 ) {
271+ continue
272+ }
273+ if ! valueDependsOn (request , requestValue , 0 ) {
274+ continue
275+ }
276+ return true
277+ }
278+ }
279+
280+ return false
281+ }
282+
85283func findHandlerRequestAndWriterParams (fn * ssa.Function ) (* ssa.Parameter , * ssa.Parameter ) {
86284 if fn == nil {
87285 return nil , nil
0 commit comments