Live data from Hacker News

Parse, Don’t Validate – Some C Safety Tips

lelanthran.com

61–70 of 78 posts

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

#62
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…

> The whole "prevent double free" claim is completely bogus. The way I interpreted the author's intent was that, the logic of error handling (something C sucks even more at) can be greatly simplified if your cleanup routine can freely be called multiple times. At the moment an error happens you no longer have to keep track of where you are in the lifecycle of each local variable, you can just call cleanup() on everyt…

That seemed kind of dubious to me as well but setting the pointer to the freed memory to NULL is good, maybe. Though, with their design, I think it would cause problems with passing the address of a stack allocated wrapper struct to the constructor function if one were into that sort of thing.

I was reading something a day or two ago where they were talking about using freed memory and their 'solution' to the problem was, basically, if the memory location wasn't reassigned to after it was freed it was 'resurrected' as a valid memory allocation. I'm fairly certain that won't ever lead to any impossible to diagnose bugs...

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

#63
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…

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

"Parse, don't validate" doesn't mean that you must encode everything in the type system -- in fact I'd argue you should usually only create new types for data (or pieces of data) that make sense for your business logic.

Here the type your business logic cares about is maybe "file valid for upload", and it is perfectly fine to have a function that takes a file, perform a bunch of checks on it, and returns a "file valid for upload" new type if it passes the checks.

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

#64
post #56

Earlier quoted context omitted.

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.

In pure C however, you still get the types-in-source-code explosion, for lack of parametric polymorphism. You need an email_or_error and a name_or_error, etc. The alternative is to fake PP with a void*, but that's so ugly I think I'd scrap the whole effort and just use char*. > I believe “Parse, don’t validate” is about handling input, not an admonition to never perform validation. It's about validation happening at…

> You need an email_or_error and a name_or_error, etc.

You don't need that. A practical solution is a generic `error` type that you return (with a special value for "no error") and `name` or `email` output arguments that only get set if there's no error.

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

#65
While this is the style I do in all of my (web) applications, it took a while to build my framework/tooling that overcomes the boilerplate to parse all incoming data into value objects. In a typical application of mine, there is usually well over dozens of entities and hundreds to thousands of fields. Creating value objects manually for all stringly types and numeric fields is a lot of work. Most value objects are some simple validation like limit to 255 characters or ints of some range.

I solve for this by using reflection and auto generating all value objects (inheriting by default from base types) and auto generating all accessor/controller classes or methods into the domain model. Therefore I model in base types, override the generated value object constructors for validation (if required), and all of the boundaries are using value objects. The internal code generally works with the underlying base types, because boxing/unboxing the value objects can be non-negligible performance impact when serializing a lot of data (which tends to be common in web applications... SQL > JSON > HTML).

I'm a huge fan, but I think ymmv. Web applications tend to have a wide interface (much of the domain model is user accessible). I think it's ideal for this case because of the number of fields a user can ultimately set and reused across many places.

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

#66
In general I'm a huge favor of using the type system to its fullest extend to protect you from messing up. If the compiler can protect me, it should.

Examples where I've used it in the past: ValidatedEmail, which is a special form of Email, one that has been validated by the user.

We can have actions that require a `PriviligedUser`, which can be created from a `User`. That creation validates ONCE whether your user is privileged.

This saves you from a whole bunch of .is_priviliged() calls in your admin panel.

The post 'Boolean Blindness' [0] talks about much of the same issues.

[0]: https://existentialtype.wordpress.com/2011/03/15/boolean-bli...

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

#68

In general I'm a huge favor of using the type system to its fullest extend to protect you from messing up. If the compiler can protect me, it should. Examples where I've used it in the past: ValidatedEmail, which is a special form of Email, one that has been validated by the user. We can have actions that require a `PriviligedUser`, which can be created from a `User`. That creation validates ONCE whether your user is…

I'm a big proponent of using types, but my experience with "Validated" types has not been positive. It's nice when it's unambiguous, but adding "validated" to the name of a type can mean almost anything. What has been validated? Validated according to what rules? You can add what has been validated to the type name, like I can tell that ValidatedNonnegativeInt is a nonnegative integer, but NonnegativeInt would tell me the same thing more concisely.

I've also had a negative experience with using types to encode privileges. Types work well in simple situations, but they scale very badly with additional complexity. Something like PrivilegedUser works fine as long as privilege is binary and one-dimensional, but the need for new types will very quickly grow out of hand for only a modest increase in the complexity of requirements. Encoding privileges as data handles a combinatorial explosion of possibilities much more gracefully, and it is much more straightforward for checking rules that are stored outside the codebase.

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

#69
post #42

Earlier quoted context omitted.

"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 whe…

thanks for clarifying.

to all of this I agree:

if the linker doesn't have namespaces (and it doesn't, unlike, say, the Java class loader, or even more extravagant the OSGi bundle loading mechanism), you need to flatten names into one name space. Which means, as you say, name mangling. and that, even without overloading, is a major PITA.

and indeed, not prescribing a prefix and just blocking a useful suffix was also an idea others hopefully took as inspiration how to not do things...

wrt prefixes in the C stdlib, I'd strictly prefer the prefix to be '#define' able, so _if_ you need to move th stdlib to a namespace, #define the prefix before #include-ing the library. needs a statically linked trampoline, though or some other nasty lionk time mechanism. meh. there is a reason languages come with namespaces from the start, these days ...

Post reply on HN