I don’t really understand what the author dislikes about the pthread API. It’s easier to bind than straight macros and safer than strings. What I really dislike about the string solution is: what do I do during typos? Does it ignore the error? Does it abort? If it aborts then how do I guarantee I’ve tested that path? C has so few guarantees and a string interface gives up any of the ones that might be useful.
My personal preference would be:
tame(tame_stdio() | tame_cmsg() | tame_getpw() | tame_proc() | tame_dns(), NULL);
It’s the same as the first example except the macro values are behind functions, which makes it easier to bind. I wish all APIs put their macros behind functions.
Using APIs like pthread_create makes me think I’m programming a bytecode interpreter by hand. Each call is like emit_opcode(LOAD); emit_opcode(STORE); … Take a look at the posix_spawn API sometime. It really is a mini program that you have to write, emitting one operation at a time. If I’m going to write a program, I’d rather work with something higher level.
If I’m going to write a program, I’d rather work with something higher level.
Let’s not kid ourselves, though: you’re writing in C. You’re high level as a mouse is relative to an insect. The pthread interface is explicit and about as secure as you can hope for in C. The string interface, on the other hand, is building a small interpreted language into a function call. Going from taking some opaque type to a pointer, which could contain invalid code in this interpreted language. The interpreter is now a security vulnerability and constructing a proper one of these dynamically is an attack target. And this, for what? Saving a few keystrokes in a language that is already not very expressive and insecure? I just don’t see the win.
Oh, I don’t think you should use it if you don’t like it. I happened to be thinking about it, and it seemed like something many people had never even considered.
Oh, I don’t think you should use it if you don’t like it.
Given that I think it drastically and unnecessarily increases the attack surface of an API, I’m more concerned with other people using it :)
This is a good point, but it bears some further exploration. What does it mean to attack the API and who’s going to do it?
Both the pthread and glx examples would run at the same privilege level as the calling code. The programmer can only hurt themselves.
If you are concerned that a naughty user will provide the program with a malformed input to pthread create I think you should be more worried that your current design apparently allows an attacker to pick their own stack size.
If untrusted input is flowing to these functions today (perhaps in the case of glx, or setcipher), then it is already being parsed. By you. By me. By Tom, Dick, and Harry. I think it’s more likely that one programmer can get this right than that every programmer can get this right.
The tame interface is the only one that actually involves a security barrier. And yes, we’d best hope we get that right. But if, as we believe, this makes it easier to visually inspect 200 some tame() calls in the code, then maybe we’ve come out ahead.
These are nice when the options are all known at compile-time, but they become a hassle as soon as you need to start putting options together programmatically (which is quite a large chunk of the time!). Suddenly every interface has a possible injection attack vector.
(To put it another way, DALs are basically the inverse of this for SQL, and using them — or something like them, like ORMs — is usually advisable. Of course, tame is the perfect use-case for this, as something that shouldn’t vary except by the programmer at compile-time, but I’m not sure about the other examples.)
In other languages this problem is solved with keyword arguments and optional arguments with default values. This is clearest in this example:
pthread_magic_create(&thread, "stack:4096 guard:0 detached", ...
Can C be extended so that it supports (roughly) this, without the double quotes?
(Second thought, why isn’t taking a struct as an argument an option?)
Passing a struct is an interesting option. That does solve many cases, but can make FFI even more annoying.
Wouldn’t a struct impose ABI incompatibilities more often than both macros & a string interface?
I think libraries should try to add function calls to manipulate structs rather than just the raw struct. It makes binding much easier.
how does that differ then to the pthread API? That looks exactly like functions to manipulate an opaque struct pointer.
I don’t dislike the pthread solution. I think it’s the right idea.
As easy as a C string? :)