Live data from Hacker News

Parse, Don’t Validate – Some C Safety Tips

lelanthran.com

41–50 of 78 posts

Re: Parse, Don’t Validate – Some C Safety Tips

#42
post #3

`_t` should not be used for custom types since it's reserved for future standard types (and/or types declared in a header you might include someday). This does cause real-world problems (`key_t` anyone?). Gratuitous allocations are gratuitous. The whole "prevent double free" claim is completely bogus. Setting a variable to `NULL` only works for cases where there is one, obvious, owner, which is not the circumstance u…

> `_t` should not be used for custom types since it's reserved for future standard types (and/or types declared in a header you might include someday). That old thing again... The _t postfix is only reserved in the POSIX standard, but not in the C standard (and C and POSIX are entirely different things - outside the UNIX bubble at least). It's unlikely that POSIX changes anymore, but if you get a name collision in a…

"the UNIX bubble" is an interesting take in the context of C, given the origins of C

Is your point "why did posix not establish a prefix_ ... _suffix" combo, and maybe even better some reserved "prefix_" namespace?

which --- I think --- for better or worse leads to the reality that C doesn't have a namespace mechanism, like, say, Java.

Re: Parse, Don’t Validate – Some C Safety Tips

#43
One flaw I've seen in "Parse, Don't Validate" as it pertains to real codebases is that you end up with a combinatorial prolieration of types.

E.g., requiring that a string be base64, have a certain fixed length, and be provided by the user.

E.g., requiring that a file have the correct MIME type, not be too large, and contain no EXIF metadata.

If you really always need all n of those things then life isn't terrible (you can parse your data into some type representing the composition of all of them), but you often only need 1, 2, or 3 and simultaneously don't want to duplicate too much code or runtime work, leading to a combinatorial explosion of intermediate types and parsing code.

As one possible solution, I put together a POC in Zig [0] with one idea, where you abuse comptime to add arbitrary tagging to types, treating a type as valid if it has the subset of tags you care about. I'm very curious what other people do to appropriately model that sort of thing though.

[0] https://github.com/hmusgrave/pdv

Re: Parse, Don’t Validate – Some C Safety Tips

#44
From experience, parsing input into data structures that fit the problem domain once at the "edge" is a good idea. The code becomes a lot more maintainable without a bunch of validation checks scattered all over the place, picking a data structure for the problem at hand usually leads to cleaner solutions, and errors usually show up much earlier and are easier to debug.

From experience though I've found that wrapping all data in newtypes adds too much ceremony and boilerplate. If the data can reasonably be expressed as a primitive type, then you might as well express that way. I can't think of a time where newtype wrapping would have saved me from accidentally not validating or accidentally inputting the wrong data as a parameter. Especially the email example is quite weak, with ~30 lines of code just being ceremony due to wrapping a string, and most likely it's just going to be fed as is to various crud operations that will cast the data to a string immediately.

Interacting with Haskell/elm libraries that have pervasive use of newtypes everywhere can be painful, especially if they don't give you a way to access the internal data. If a use-case comes up that the library developer didn't account for, then you might have no way of modifying the data and you end up needing to patch the library upstream.

Re: Parse, Don’t Validate – Some C Safety Tips

#45
post #42

Earlier quoted context omitted.

> `_t` should not be used for custom types since it's reserved for future standard types (and/or types declared in a header you might include someday). That old thing again... The _t postfix is only reserved in the POSIX standard, but not in the C standard (and C and POSIX are entirely different things - outside the UNIX bubble at least). It's unlikely that POSIX changes anymore, but if you get a name collision in a…

"the UNIX bubble" is an interesting take in the context of C, given the origins of C Is your point "why did posix not establish a prefix_ ... _suffix" combo, and maybe even better some reserved "prefix_" namespace? which --- I think --- for better or worse leads to the reality that C doesn't have a namespace mechanism, like, say, Java.

Well, C does have a namespace mechanism, it's called prefixes ;) It's just unfortunate that both POSIX and the C stdlib don't use prefixes (except for the new C23 stdc_ functions which is going into the right direction at least).

The problem with C++ style namespaces as language feature is that they require name mangling, which opens up a whole new can of worms.

In the end, the POSIX _t just means "don't blame us when your library names collide with POSIX names", and that's fine. Other platforms have that problem as well, but the sky hasn't fallen because an occasional type or function name collision.

Re: Parse, Don’t Validate – Some C Safety Tips

#46
post #40

Earlier quoted context omitted.

Still debatable since the C standard doesn't reserve the _t postfix (it does reserve a single leading underscore followed by a capital letter, e.g. _Bool, and IIRC it also reserves two leading underscores). What POSIX reserves or doesn't reserve doesn't affect code that follows only the C standard but doesn't care about POSIX compatibility, and especially _t is so widely used in C libraries that POSIX's opinion obvio…

Whilst you do point to 6.4.3 for where it does reserve "All identifiers that begin with a double underscore (__) or begin with an underscore (_) followed by an uppercase letter"... That section also has the lovely: > Other identifiers may be reserved. If an implementation of C uses it... Just... Don't. The standard won't save you here, because it's happy for an implementation to do whatever they feel like.

Yeah, and that makes any reserved name rules pretty much useless anyway, e.g. anything goes until a collision actually happens, and then it needs to be fixed on the user side anyway.

Re: Parse, Don’t Validate – Some C Safety Tips

#47
post #43

One flaw I've seen in "Parse, Don't Validate" as it pertains to real codebases is that you end up with a combinatorial prolieration of types. E.g., requiring that a string be base64, have a certain fixed length, and be provided by the user. E.g., requiring that a file have the correct MIME type, not be too large, and contain no EXIF metadata. If you really always need all n of those things then life isn't terrible (y…

You might be interested in Lean's way of doing things. They have normal types (e.g. numeric types) and subtypes (e.g. numbers less than zero). An element of the subtype "numbers less than zero" can be understood as a tuple containing the actual number (which has a normal numeric type) and a proof that this specific number is indeed less than zero.

https://lean-lang.org/doc/reference/latest/Basic-Types/Subty...

Re: Parse, Don’t Validate – Some C Safety Tips

#48
post #23
post #17

Earlier quoted context omitted.

I'm with you, don't do crap like that. Always return a valid object. email_t theEmail = parseEmail(untrustedInput); if (theEmail.error != PARSE_OK) { return error; }

This is validate . You made an email-or-error type and named it email_t and then manually checked it. PDV returns an non-error-email type from the check method .

I don't understand; what is your suggested solution?

Re: Parse, Don’t Validate – Some C Safety Tips

#50
post #43

One flaw I've seen in "Parse, Don't Validate" as it pertains to real codebases is that you end up with a combinatorial prolieration of types. E.g., requiring that a string be base64, have a certain fixed length, and be provided by the user. E.g., requiring that a file have the correct MIME type, not be too large, and contain no EXIF metadata. If you really always need all n of those things then life isn't terrible (y…

Structs are representations of combinatorial types! In your file case, you could parse the input into a struct, and then accept or reject further processing based on that contents of that struct.

Of course, it would be reasonable to claim that the accept/reject step is validation, but I believe “Parse, don’t validate” is about handling input, not an admonition to never perform validation.

Post reply on HN