-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
missing value checks #78
Comments
I would like cstruct to check if values are in range -- I'm sure I've made mistakes which have gone undetected for longer because it doesn't check. Since cstruct already raises |
Should we just add a mask (such as |
It’s frustrating to have this be an overhead on constant arguments. If there’s no way to convince the optimiser to check overflow at compile time, we could consider a small ppc checker for this. A mask on every single set call for a constant should be avoided if we can, but I agree it does need to be checked at build time. |
Obviously we can infer a possible overhead but:
Let's start with this simple code: let pp_uint16 ppf x =
Format.fprintf ppf "%04x" (x land 0xffff)
[@@inline]
let () =
pp_uint16 Format.std_formatter 42 ;
pp_uint16 Format.std_formatter (Sys.argv.(1)) OCaml generates this ; pp_uint16 Format.std_formatter 42
movl $85, %ebx ; $85 is [(42 lsl 1) + 1]
movq %rbx, 8(%rsp)
movq camlExample__4@GOTPCREL(%rip), %rbx
movq %rbx, (%rsp)
call camlStdlib__format__fprintf_1166@PLT And ; pp_uint16 Format.std_formatter (int_of_string Sys.argv.(1)
; [int_of_string Sys.argv.(1)] is stored into %rax
andq $131071, %rax ; it's the inlined part [x land 0xffff] where [131071 = (0xffff lsl 1) + 1]
movq %rax, 8(%rsp)
movq camlExample__4@GOTPCREL(%rip), %rax
movq %rax, (%rsp)
movq %rbx, %rax
call camlStdlib__format__fprintf_1166@PLT The |
That looks good to me. Thanks for checking. Let's do the mask then, and prevent overflow. |
I understand masking helps to avoid the overflow(s), but with the issue from dns linked above, it will only hide the actual issue by silently truncating (a semantics I'm not too fund of, since it is pretty hard to debug). TL;DR: similar to checking offset and length on |
I think this choice should be lead by optimization done by the compiler. I'm not sure that the compiler can delete a branch if we give to it a constant which respects the range. However, again, the CPU prediction can lead on that. From my perspective, I like the idea of abstract type but we fallback to another issue: #86. |
since there are some performance notes in this issue and #86, maybe we should first establish some benchmarks (micro and real-world usage), then re-think safety/correctness and then performance again. Re-reading #86, I think abstract types (and conversions) are the way to go in respect to safety. Performance-wise, I struggle whether there should be some sort of Cstruct.Unsafe for non-checked operations (my intuition is when I receive an IPv4 packet, and check that it is at least header length bytes, I should be able to access individual header fields without performance penalty of bounds checks), i.e.: let do_something_with buf =
guard (Cstruct.len buf > Ipv4_packet.header_size) "too small" >>= fun () ->
let typ = Cstruct.get_uint8 buf 3 in (* this should not induce bounds checks since I just checked that in the line above *)
... EDIT: same for serialising: let buf = Cstruct.create 20 in
Cstruct.set_uint8 buf 0 0x2F; (* should neither bound check nor value bound check :D *) |
Should cstruct check that the value of a
set_XXX
is in range? At the moment the following assertion does not hold (set/get_uint8 is interchangable with uint16):Opinions?
The text was updated successfully, but these errors were encountered: