So, I’m curious if something like this could be implemented, because it’s such an interesting thing and would allow programmers to customize Zig to exactly how they’d like it.
The Ada programming language has this concept of restrictions and profiles. The idea is that there are restrictions, which allow the programmer to disable or alter core parts of the language or run-time environment/standard library, and there are profiles, which are sets of restrictions or other behaviors that must be obeyed. This, in turn, forms a contract of sorts between the programmer and compiler: disable this feature (i.e. floating point, threads) and I’ll agree not to use it anywhere in this code. This would be a pretty neat feature to have in Zig someday, but I thought I’d post it here since I’m not positive how it could be implemented. For example, we could have a restriction that turns off all floating-point support entirely, such that if you even try to use a function that depends on a floating-point type, or declare a variable that is of floating-point type, that would be a compilation error. I can see this having benefits in embedded systems and similar domains, where you probably don’t need FP support, or things like it, and so you also probably don’t want software FP enabled. Do yo uguys think this would be a god idea for Zig to have? I don’t know in what version it’d fit but it’d be really neat, since Ada is the only language I know of that allows you to do this kind of thing, and I wish more languages adopted it.
4 Likes
I think the Java Security Manager is similar to this.
I agree, this is a powerful feature to have. The first use-case I think of is a sandboxed web playground where std.fs
, std.net
, std.posix
, etc. are not allowed. If I recall correctly, the Go team produced a modified version of the compiler to achieve this in their web playground.
Related to this: Zig has @setRuntimeSafety(bool)
, which turns safety checks for undefined behavior on and off on a per-scope basis. I hope that, eventually, a built-in is added to make this more fine-grained, so that each case of undefined behavior which is detectable can be turned on and off.
Ideally, this would come in a dynamic-bind form as well, so that if I enable, say, bounds-checking, that anything in the entire call stack of the function will also use bounds checking. The current behavior is lexical, which is the correct default: if you turn on runtime safety, that applies to any scope which inherits from the scope with the declaration, unless one of those scopes explicitly turns it off. But this means that if I enable bounds checking and make a slice, and hand it off to a function, the function is going to do what its scope dictates. A dynamic bind would mean that the function call inherits safety checks from its call scope, not its definition/lexical scope. There are times when that would be pretty useful.
This is easier said than done, there’s a fair amount of complexity in how runtime safety is actually handled, see this thread about what happens when safety-checks are disabled at the definition site of a bare union
.
But it would come with some considerable advantages. It would be great to be in a position to define a buffer array on the stack, and then turn on bounds checks in a way which applies to every down-stack function call. That would prevent stack-smashing attacks, which are one of the most common security vulnerabilities.