Data_encoding: early detection of some oversized data
This commit is contained in:
parent
a5cec8fca0
commit
e3272bebc5
@ -218,11 +218,11 @@ let rec skip n state k =
|
|||||||
(** Main recursive reading function, in continuation passing style. *)
|
(** Main recursive reading function, in continuation passing style. *)
|
||||||
let rec read_rec
|
let rec read_rec
|
||||||
: type next ret.
|
: type next ret.
|
||||||
next Encoding.t -> state -> ((next * state) -> ret status) -> ret status
|
bool -> next Encoding.t -> state -> ((next * state) -> ret status) -> ret status
|
||||||
= fun e state k ->
|
= fun whole e state k ->
|
||||||
let resume buffer =
|
let resume buffer =
|
||||||
let stream = Binary_stream.push buffer state.stream in
|
let stream = Binary_stream.push buffer state.stream in
|
||||||
try read_rec e { state with stream }k
|
try read_rec whole e { state with stream }k
|
||||||
with Read_error err -> Error err in
|
with Read_error err -> Error err in
|
||||||
let open Encoding in
|
let open Encoding in
|
||||||
assert (Encoding.classify e <> `Variable || state.remaining_bytes <> None) ;
|
assert (Encoding.classify e <> `Variable || state.remaining_bytes <> None) ;
|
||||||
@ -251,7 +251,7 @@ let rec read_rec
|
|||||||
let size = remaining_bytes state in
|
let size = remaining_bytes state in
|
||||||
Atom.fixed_length_string size resume state k
|
Atom.fixed_length_string size resume state k
|
||||||
| Padded (e, n) ->
|
| Padded (e, n) ->
|
||||||
read_rec e state @@ fun (v, state) ->
|
read_rec false e state @@ fun (v, state) ->
|
||||||
skip n state @@ (fun state -> k (v, state))
|
skip n state @@ (fun state -> k (v, state))
|
||||||
| RangedInt { minimum ; maximum } ->
|
| RangedInt { minimum ; maximum } ->
|
||||||
Atom.ranged_int ~minimum ~maximum resume state k
|
Atom.ranged_int ~minimum ~maximum resume state k
|
||||||
@ -263,49 +263,49 @@ let rec read_rec
|
|||||||
read_list e state @@ fun (l, state) ->
|
read_list e state @@ fun (l, state) ->
|
||||||
k (Array.of_list l, state)
|
k (Array.of_list l, state)
|
||||||
| List e -> read_list e state k
|
| List e -> read_list e state k
|
||||||
| (Obj (Req { encoding = e })) -> read_rec e state k
|
| (Obj (Req { encoding = e })) -> read_rec whole e state k
|
||||||
| (Obj (Dft { encoding = e })) -> read_rec e state k
|
| (Obj (Dft { encoding = e })) -> read_rec whole e state k
|
||||||
| (Obj (Opt { kind = `Dynamic ; encoding = e })) ->
|
| (Obj (Opt { kind = `Dynamic ; encoding = e })) ->
|
||||||
Atom.bool resume state @@ fun (present, state) ->
|
Atom.bool resume state @@ fun (present, state) ->
|
||||||
if not present then
|
if not present then
|
||||||
k (None, state)
|
k (None, state)
|
||||||
else
|
else
|
||||||
read_rec e state @@ fun (v, state) ->
|
read_rec whole e state @@ fun (v, state) ->
|
||||||
k (Some v, state)
|
k (Some v, state)
|
||||||
| (Obj (Opt { kind = `Variable ; encoding = e })) ->
|
| (Obj (Opt { kind = `Variable ; encoding = e })) ->
|
||||||
let size = remaining_bytes state in
|
let size = remaining_bytes state in
|
||||||
if size = 0 then
|
if size = 0 then
|
||||||
k (None, state)
|
k (None, state)
|
||||||
else
|
else
|
||||||
read_rec e state @@ fun (v, state) ->
|
read_rec whole e state @@ fun (v, state) ->
|
||||||
k (Some v, state)
|
k (Some v, state)
|
||||||
| Objs { kind = `Fixed sz ; left ; right } ->
|
| Objs { kind = `Fixed sz ; left ; right } ->
|
||||||
ignore (check_remaining_bytes state sz : int option) ;
|
ignore (check_remaining_bytes state sz : int option) ;
|
||||||
ignore (check_allowed_bytes state sz : int option) ;
|
ignore (check_allowed_bytes state sz : int option) ;
|
||||||
read_rec left state @@ fun (left, state) ->
|
read_rec false left state @@ fun (left, state) ->
|
||||||
read_rec right state @@ fun (right, state) ->
|
read_rec whole right state @@ fun (right, state) ->
|
||||||
k ((left, right), state)
|
k ((left, right), state)
|
||||||
| Objs { kind = `Dynamic ; left ; right } ->
|
| Objs { kind = `Dynamic ; left ; right } ->
|
||||||
read_rec left state @@ fun (left, state) ->
|
read_rec false left state @@ fun (left, state) ->
|
||||||
read_rec right state @@ fun (right, state) ->
|
read_rec whole right state @@ fun (right, state) ->
|
||||||
k ((left, right), state)
|
k ((left, right), state)
|
||||||
| Objs { kind = `Variable ; left ; right } ->
|
| Objs { kind = `Variable ; left ; right } ->
|
||||||
read_variable_pair left right state k
|
read_variable_pair left right state k
|
||||||
| Tup e -> read_rec e state k
|
| Tup e -> read_rec whole e state k
|
||||||
| Tups { kind = `Fixed sz ; left ; right } ->
|
| Tups { kind = `Fixed sz ; left ; right } ->
|
||||||
ignore (check_remaining_bytes state sz : int option) ;
|
ignore (check_remaining_bytes state sz : int option) ;
|
||||||
ignore (check_allowed_bytes state sz : int option) ;
|
ignore (check_allowed_bytes state sz : int option) ;
|
||||||
read_rec left state @@ fun (left, state) ->
|
read_rec false left state @@ fun (left, state) ->
|
||||||
read_rec right state @@ fun (right, state) ->
|
read_rec whole right state @@ fun (right, state) ->
|
||||||
k ((left, right), state)
|
k ((left, right), state)
|
||||||
| Tups { kind = `Dynamic ; left ; right } ->
|
| Tups { kind = `Dynamic ; left ; right } ->
|
||||||
read_rec left state @@ fun (left, state) ->
|
read_rec false left state @@ fun (left, state) ->
|
||||||
read_rec right state @@ fun (right, state) ->
|
read_rec whole right state @@ fun (right, state) ->
|
||||||
k ((left, right), state)
|
k ((left, right), state)
|
||||||
| Tups { kind = `Variable ; left ; right } ->
|
| Tups { kind = `Variable ; left ; right } ->
|
||||||
read_variable_pair left right state k
|
read_variable_pair left right state k
|
||||||
| Conv { inj ; encoding } ->
|
| Conv { inj ; encoding } ->
|
||||||
read_rec encoding state @@ fun (v, state) ->
|
read_rec whole encoding state @@ fun (v, state) ->
|
||||||
k (inj v, state)
|
k (inj v, state)
|
||||||
| Union { tag_size ; cases } -> begin
|
| Union { tag_size ; cases } -> begin
|
||||||
Atom.tag tag_size resume state @@ fun (ctag, state) ->
|
Atom.tag tag_size resume state @@ fun (ctag, state) ->
|
||||||
@ -318,7 +318,7 @@ let rec read_rec
|
|||||||
with
|
with
|
||||||
| exception Not_found -> Error (Unexpected_tag ctag)
|
| exception Not_found -> Error (Unexpected_tag ctag)
|
||||||
| Case { encoding ; inj } ->
|
| Case { encoding ; inj } ->
|
||||||
read_rec encoding state @@ fun (v, state) ->
|
read_rec whole encoding state @@ fun (v, state) ->
|
||||||
k (inj v, state)
|
k (inj v, state)
|
||||||
end
|
end
|
||||||
| Dynamic_size { kind ; encoding = e } ->
|
| Dynamic_size { kind ; encoding = e } ->
|
||||||
@ -326,7 +326,7 @@ let rec read_rec
|
|||||||
let remaining = check_remaining_bytes state sz in
|
let remaining = check_remaining_bytes state sz in
|
||||||
let state = { state with remaining_bytes = Some sz } in
|
let state = { state with remaining_bytes = Some sz } in
|
||||||
ignore (check_allowed_bytes state sz : int option) ;
|
ignore (check_allowed_bytes state sz : int option) ;
|
||||||
read_rec e state @@ fun (v, state) ->
|
read_rec true e state @@ fun (v, state) ->
|
||||||
if state.remaining_bytes <> Some 0 then
|
if state.remaining_bytes <> Some 0 then
|
||||||
Error Extra_bytes
|
Error Extra_bytes
|
||||||
else
|
else
|
||||||
@ -337,8 +337,14 @@ let rec read_rec
|
|||||||
match state.allowed_bytes with
|
match state.allowed_bytes with
|
||||||
| None -> limit
|
| None -> limit
|
||||||
| Some current_limit -> min current_limit limit in
|
| Some current_limit -> min current_limit limit in
|
||||||
|
begin
|
||||||
|
match state.remaining_bytes with
|
||||||
|
| Some remaining when whole && limit < remaining ->
|
||||||
|
raise Size_limit_exceeded
|
||||||
|
| _ -> ()
|
||||||
|
end ;
|
||||||
let state = { state with allowed_bytes = Some limit } in
|
let state = { state with allowed_bytes = Some limit } in
|
||||||
read_rec e state @@ fun (v, state) ->
|
read_rec whole e state @@ fun (v, state) ->
|
||||||
let allowed_bytes =
|
let allowed_bytes =
|
||||||
match old_allowed_bytes with
|
match old_allowed_bytes with
|
||||||
| None -> None
|
| None -> None
|
||||||
@ -350,10 +356,10 @@ let rec read_rec
|
|||||||
let read = limit - remaining in
|
let read = limit - remaining in
|
||||||
Some (old_limit - read) in
|
Some (old_limit - read) in
|
||||||
k (v, { state with allowed_bytes })
|
k (v, { state with allowed_bytes })
|
||||||
| Describe { encoding = e } -> read_rec e state k
|
| Describe { encoding = e } -> read_rec whole e state k
|
||||||
| Splitted { encoding = e } -> read_rec e state k
|
| Splitted { encoding = e } -> read_rec whole e state k
|
||||||
| Mu { fix } -> read_rec (fix e) state k
|
| Mu { fix } -> read_rec whole (fix e) state k
|
||||||
| Delayed f -> read_rec (f ()) state k
|
| Delayed f -> read_rec whole (f ()) state k
|
||||||
|
|
||||||
and remaining_bytes { remaining_bytes } =
|
and remaining_bytes { remaining_bytes } =
|
||||||
match remaining_bytes with
|
match remaining_bytes with
|
||||||
@ -371,18 +377,18 @@ and read_variable_pair
|
|||||||
let size = remaining_bytes state in
|
let size = remaining_bytes state in
|
||||||
match Encoding.classify e1, Encoding.classify e2 with
|
match Encoding.classify e1, Encoding.classify e2 with
|
||||||
| (`Dynamic | `Fixed _), `Variable ->
|
| (`Dynamic | `Fixed _), `Variable ->
|
||||||
read_rec e1 state @@ fun (left, state) ->
|
read_rec false e1 state @@ fun (left, state) ->
|
||||||
read_rec e2 state @@ fun (right, state) ->
|
read_rec true e2 state @@ fun (right, state) ->
|
||||||
k ((left, right), state)
|
k ((left, right), state)
|
||||||
| `Variable, `Fixed n ->
|
| `Variable, `Fixed n ->
|
||||||
if n > size then
|
if n > size then
|
||||||
Error Not_enough_data
|
Error Not_enough_data
|
||||||
else
|
else
|
||||||
let state = { state with remaining_bytes = Some (size - n) } in
|
let state = { state with remaining_bytes = Some (size - n) } in
|
||||||
read_rec e1 state @@ fun (left, state) ->
|
read_rec true e1 state @@ fun (left, state) ->
|
||||||
assert (state.remaining_bytes = Some 0) ;
|
assert (state.remaining_bytes = Some 0) ;
|
||||||
let state = { state with remaining_bytes = Some n } in
|
let state = { state with remaining_bytes = Some n } in
|
||||||
read_rec e2 state @@ fun (right, state) ->
|
read_rec true e2 state @@ fun (right, state) ->
|
||||||
assert (state.remaining_bytes = Some 0) ;
|
assert (state.remaining_bytes = Some 0) ;
|
||||||
k ((left, right), state)
|
k ((left, right), state)
|
||||||
| _ -> assert false (* Should be rejected by [Encoding.Kind.combine] *)
|
| _ -> assert false (* Should be rejected by [Encoding.Kind.combine] *)
|
||||||
@ -396,12 +402,12 @@ and read_list
|
|||||||
if size = 0 then
|
if size = 0 then
|
||||||
k (List.rev acc, state)
|
k (List.rev acc, state)
|
||||||
else
|
else
|
||||||
read_rec e state @@ fun (v, state) ->
|
read_rec false e state @@ fun (v, state) ->
|
||||||
loop state (v :: acc) in
|
loop state (v :: acc) in
|
||||||
loop state []
|
loop state []
|
||||||
|
|
||||||
let read_rec e state k =
|
let read_rec e state k =
|
||||||
try read_rec e state k
|
try read_rec false e state k
|
||||||
with Read_error err -> Error err
|
with Read_error err -> Error err
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,11 +161,11 @@ let test_bounded_string_list =
|
|||||||
test "a" ~total:0 ~elements:0 [""] 4 4 @
|
test "a" ~total:0 ~elements:0 [""] 4 4 @
|
||||||
test "b1" ~total:3 ~elements:4 [""] 4 4 @
|
test "b1" ~total:3 ~elements:4 [""] 4 4 @
|
||||||
test "b2" ~total:4 ~elements:3 [""] 4 4 @
|
test "b2" ~total:4 ~elements:3 [""] 4 4 @
|
||||||
test "c1" ~total:19 ~elements:4 ["";"";"";"";""] 20 4 @
|
test "c1" ~total:19 ~elements:4 ["";"";"";"";""] 4 4 @
|
||||||
test "c2" ~total:20 ~elements:3 ["";"";"";"";""] 4 4 @
|
test "c2" ~total:20 ~elements:3 ["";"";"";"";""] 4 4 @
|
||||||
test "d1" ~total:20 ~elements:5 ["";"";"";"";"a"] 24 4 @
|
test "d1" ~total:20 ~elements:5 ["";"";"";"";"a"] 4 4 @
|
||||||
test "d2" ~total:21 ~elements:4 ["";"";"";"";"a"] 24 24 @
|
test "d2" ~total:21 ~elements:4 ["";"";"";"";"a"] 24 24 @
|
||||||
test "e" ~total:30 ~elements:10 ["ab";"c";"def";"gh";"ijk"] 32 4
|
test "e" ~total:30 ~elements:10 ["ab";"c";"def";"gh";"ijk"] 4 4
|
||||||
|
|
||||||
let tests =
|
let tests =
|
||||||
all_ranged_int 100 400 @
|
all_ranged_int 100 400 @
|
||||||
|
Loading…
Reference in New Issue
Block a user