Live data from Hacker News

Static Allocation with Zig

nickmonad.blog

71–80 of 112 posts

Re: Static Allocation with Zig

#71
post #64
post #35

Earlier quoted context omitted.

> had difficulty being adopted more broadly Most applications don’t need to bother the user with things like how much memory they think will be needed upfront. They just allocate how much and when necessary. Most applications today are probably servers that change all the time. You would not know upfront how much memory you’d need as that would keep changing on every release! Static allocation may work in a few domai…

That's just saying "we push our memory problems up the stack so our clients / users need to deal with that". The reason this works is because human users in particular have become accustomed to software being buggy and failing often.

What ?? It’s exactly the opposite of that! Memory allocation on demand frees users from having to worry about configuring memory settings, which static allocation requires except if you overallocate, which is problematic if lots of applications start doing. I absolutely don’t like the argument that memory is nearly free! Most laptops still come with around 8GB of RAM which a browser by itself can consume already … there’s really not a lot left when you also got Docker, compilers, music app, email and so on running. I have 64GB and still have to close apps sometimes because software nowadays does stupid things like overallocating. Don’t do that.

Re: Static Allocation with Zig

#72
post #14

> All memory must be statically allocated at startup. But why? If you do that you are just taking memory away from other processes. Is there any significant speed improvement over just dynamic allocation?

1. On modern OSes, you probably aren't "taking it away from other processes" until you actually use it. Statically allocated but untouched memory is probably just an entry in a page table somewhere. 2. Speed improvement? No. The improvement is in your ability to reason about memory usage, and about time usage. Dynamic allocations add a very much non-deterministic amount of time to whatever you're doing.

Use mlock as long as it is allocated it is going to be rather deterministic, of course you might be running in a VM on an over commited host. I guess you can "prefault" in a busy loop instead of only on startup, waste memory and cpu!

Re: Static Allocation with Zig

#73
post #20

Earlier quoted context omitted.

I’m confused. How is a program that uses static allocation not Turing complete?

A Turing machine has an unlimited tape. You can’t emulate it with a fixed amount of memory. It’s mostly a theoretical issue, though, because all real computer systems have limits. It’s just that in languages that assume unlimited memory, the limits aren’t written down. It’s not “part of the language.”

What about IO? Just because I have a statically allocated program with a fixed amount of memory doesn’t mean I can’t do IO. My fixed memory can just be a cache / scratchpad and the unlimited tape can work via IO (disk, network, etc).

Re: Static Allocation with Zig

#75
post #62

Earlier quoted context omitted.

Those guidelines are quite clear that they're written specifically in the context of the C programming language, and may not make sense in other contexts: "For fairly pragmatic reasons, then, our coding rules primarily target C and attempt to optimize our ability to more thoroughly check the reliability of critical applications written in C." A version of this document targeting, say, Ada would look quite different.

The JPL C rules are quite old, but avoiding dynamic allocation outside initialization is am considered best practice for spaceflight software regardless of language. Here's the recommendation from NASA's language-agnostic cFS: 4.2.4 Consolidate Resource Allocations It is generally recommended to consolidate resource allocations to the application initialization function(s). Allocations and setup of resources such as…

> The JPL C rules are quite old, but avoiding dynamic allocation outside initialization is am considered best practice for spaceflight software regardless of language.

The rules are written with the historical context of C making it too easy to leak heap-allocated memory. In the safety-critical Rust code that I've worked on, we tend not to dynamically allocate due to the usual constraints, and we're well aware of the "thou shalt not allocate" rules in the scripture, but we've already gotten clearance from the relevant certification authorities that Rust is exempt from the restriction against dynamic allocation specifically because of its ownership system.

Re: Static Allocation with Zig

#76
post #10

Personally I believe static allocation has pretty huge consequences for theoretical computer science. It’s the only kind of program that can be actually reasoned about. Also, not exactly Turing complete in classic sense. Makes my little finitist heart get warm and fuzzy.

> It’s the only kind of program that can be actually reasoned about.

No. That is one restriction that allows you to theoretically escape the halting problem, but not the only one. Total functional programming languages for example do it by restricting recursion to a weaker form.

Also, more generally, we can reason about plenty of programs written in entirely Turing complete languages/styles. People keep mistaking the halting problem as saying that we can never successfully do termination analysis on any program. We can, on many practical programs, including ones that do dynamic allocations.

Conversely, there are programs that use only a statically bounded amount of memory for which this analysis is entirely out of reach. For example, you can write one that checks the Collatz conjecture for the first 2^1000 integers that only needs about a page of memory.

Re: Static Allocation with Zig

#77

> All memory must be statically allocated at startup. No memory may be dynamically allocated (or freed and reallocated) after initialization. This avoids unpredictable behavior that can significantly affect performance, and avoids use-after-free. As a second-order effect, it is our experience that this also makes for more efficient, simpler designs that are more performant and easier to maintain and reason about, com…

Static allocation has been around for a long time but few people consider it even in contexts where it makes a lot of sense. I’ve designed a few database engines that used pure static allocation and developers often chafe at this model because it seems easier to delegate allocation (which really just obscures the complexity). Allocation aside, many optimizations require knowing precisely how close to instantaneous re…

I've always thought static allocation was why we got overcommit[1] in Linux and its infamous OOM killer. In the 1990s big boy commercial databases assumed specialized admins, and one of their tasks was to figure out the value for the memory allocation setting in the DB configuration, which the DB would immediately allocate on startup. As a magic value, the easiest path forward was just to specify most of your RAM. DBs used to run on dedicated machines, anyhow. But then Linux came along and democratized running servers, and people wanted to run big boy databases alongside other services like Apache. Without overcommit these databases wouldn't run as typically configured--"best practice" allocation advice used up too much memory, leaving nothing for the rest of the services, especially on the more memory-constrained machines people ran Linux. Because on a typical system most of the memory preallocated to the DB was never used anyhow (the figure wasn't actually carefully chosen as intended), or the DB was designed (or at least the manual's written) with bigger machines in mind, and Linus wanted things to Just Work, whether experienced admins or not, the easy fix was just to overcommit in the kernel, et voila, a pain point for people dabbling with Linux was solved, at least superficially.

NB: I was just a newbie back then, so any older grey beards, please feel free to correct me. But I distinctly remember supporting commercial databases as being one of the justifications for overcommit, despite overcommit not being typical in the environments originally running those DBs, AFAIU.

[1] Note that AFAIU the BSDs had overcommit, too, but just for fork + CoW. Though these days FreeBSD at least has overcommit more similar to Linux. Solaris actually does strict accounting even for fork, and I assume that was true back in the 90s. Did any commercial Unices actually do overcommit by default?

Re: Static Allocation with Zig

#78
post #65
post #59

Earlier quoted context omitted.

See https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TI... for motivation. - Operational predictability --- latencies stay put, the risk of threshing is reduced (_other_ applications on the box can still misbehave, but you are probably using a dedicated box for a key database) - Forcing function to avoid use-after-free. Zig doesn't have a borrow checker, so you need something else in its place. Static alloca…

> Forcing function to avoid use-after-free Doesn't reusing memory effectively allow for use-after-free, only at the progam level (even with a borrow checker)?

Yes, kind of. In the same sense that Vec in Rust with reused indexes allows it.

Notice that this kind of use-after-free is a ton more benign though. This milder version upholds type-safety and what happens can be reasoned about in terms of the semantics of the source language. Classic use-after-free is simply UB in the source language and leaves you with machine semantics, usually allowing attackers to reach arbitrary code execution in one way or another.

Re: Static Allocation with Zig

#79
post #78
post #65

Earlier quoted context omitted.

> Forcing function to avoid use-after-free Doesn't reusing memory effectively allow for use-after-free, only at the progam level (even with a borrow checker)?

Yes, kind of. In the same sense that Vec in Rust with reused indexes allows it. Notice that this kind of use-after-free is a ton more benign though. This milder version upholds type-safety and what happens can be reasoned about in terms of the semantics of the source language. Classic use-after-free is simply UB in the source language and leaves you with machine semantics, usually allowing attackers to reach arbitrar…

That what happens can be reasoned about in the semantics of the source language as opposed to being UB doesn't necessarily make the problem "a ton more benign". After all, a program written in Assembly has no UB and all of its behaviours can be reasoned about in the source language, but I'd hardly trust Assembly programs to be more secure than C programs [1]. What makes the difference isn't that it's UB but, as you pointed out, the type safety. But while the less deterministic nature of a "malloc-level" UAF does make it more "explosive", it can also make it harder to exploit reliably. It's hard to compare the danger of a less likely RCE with a more likely data leak.

On the other hand, the more empirical, though qualitative, claim made by by matklad in the sibling comment may have something to it.

[1]: In fact, take any C program with UB, compile it, and get a dangerous executable. Now disassemble the executable, and you get an equally dangerous program, yet it doesn't have any UB. UB is problematic, of course, partly because at least in C and C++ it can be hard to spot, but it doesn't, in itself, necessarily make a bug more dangerous. If you look at MITRE's top 25 most dangerous software weaknesses, the top four (in the 2025 list) aren't related to UB in any language (by the way, UAF is #7).

Re: Static Allocation with Zig

#80
post #69
post #65

Earlier quoted context omitted.

> Forcing function to avoid use-after-free Doesn't reusing memory effectively allow for use-after-free, only at the progam level (even with a borrow checker)?

There's some reshuffling of bugs for sure, but, from my experience, there's also a very noticeable reduction! It seems there's no law of conservation of bugs. I would say the main effect here is that global allocator often leads to ad-hoc, "shotgun" resource management all other the place, and that's hard to get right in a manually memory managed language. Most Zig code that deals with allocators has resource managem…

That's an interesting observation. BTW, I've noticed that when I write in Assembly I tend to have fewer bugs than when I write in C++ (and they tend to be easier to find). That's partly because I'm more careful, but also because I only write much shorter and simpler things in Assembly.
Post reply on HN