3636 ErrLeaseHTTPTimeout = errors .New ("waiting for node to catch up its applied index has timed out" )
3737)
3838
39+ // maxLeaseHTTPRequestSize bounds how much of a request body ServeHTTP will
40+ // read into memory. Legitimate LeaseKeepAliveRequest and LeaseInternalRequest
41+ // messages only carry a lease ID (and a bool flag), so this is generous
42+ // headroom, not a realistic size. Matches connReadLimitByte in
43+ // server/etcdserver/api/rafthttp/http.go for consistency.
44+ const maxLeaseHTTPRequestSize = 64 * 1024
45+
3946// NewHandler returns an http Handler for lease renewals
4047func NewHandler (l lease.Lessor , waitch func () <- chan struct {}) http.Handler {
4148 return & leaseHandler {l , waitch }
@@ -53,8 +60,13 @@ func (h *leaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
5360 }
5461
5562 defer r .Body .Close ()
56- b , err := io .ReadAll (r .Body )
63+ b , err := io .ReadAll (http . MaxBytesReader ( w , r .Body , maxLeaseHTTPRequestSize ) )
5764 if err != nil {
65+ var maxBytesErr * http.MaxBytesError
66+ if errors .As (err , & maxBytesErr ) {
67+ http .Error (w , "request body too large" , http .StatusRequestEntityTooLarge )
68+ return
69+ }
5870 http .Error (w , "error reading body" , http .StatusBadRequest )
5971 return
6072 }
@@ -262,8 +274,24 @@ func TimeToLiveHTTP(ctx context.Context, id lease.LeaseID, keys bool, url string
262274 return lresp , nil
263275}
264276
277+ // maxLeaseHTTPResponseSize bounds how much of a lease HTTP response body
278+ // readResponse will read into memory. Unlike requests, a legitimate
279+ // LeaseInternalResponse can carry every key attached to a lease, so this
280+ // ceiling is deliberately generous -- it only guards against a misbehaving
281+ // or compromised peer forcing unbounded memory growth, not against large but
282+ // legitimate responses.
283+ const maxLeaseHTTPResponseSize = 100 * 1024 * 1024
284+
265285func readResponse (resp * http.Response ) (b []byte , err error ) {
266- b , err = io .ReadAll (resp .Body )
286+ b , err = io .ReadAll (io .LimitReader (resp .Body , maxLeaseHTTPResponseSize + 1 ))
287+ if err != nil {
288+ httputil .GracefulClose (resp )
289+ return nil , err
290+ }
291+ if len (b ) > maxLeaseHTTPResponseSize {
292+ resp .Body .Close ()
293+ return nil , fmt .Errorf ("lease: response body exceeds %d bytes limit" , maxLeaseHTTPResponseSize )
294+ }
267295 httputil .GracefulClose (resp )
268- return
296+ return b , nil
269297}
0 commit comments