A *C*atastrope in OpenZFS
2025-07-21
So yesterday, I was going through some tech news and stuff, and something spotted my eyes. An almost catastrophic OpenZFS bug and the humans that made it
So this is the code that made the bug happen:
/*
* This code converts an asize into the largest psize that can safely be written
* to an allocation of that size for this vdev.
*
* Note that this function will not take into account the effect of gang
* headers, which also modify the ASIZE of the DVAs. It is purely a reverse of
* the psize_to_asize function.
*/
static uint64_t
vdev_raidz_asize_to_psize(vdev_t *vd, uint64_t asize, uint64_t txg)
{
vdev_raidz_t *vdrz = vd->vdev_tsd;
uint64_t psize;
uint64_t ashift = vd->vdev_top->vdev_ashift;
uint64_t cols = vdrz->vd_original_width;
uint64_t nparity = vdrz->vd_nparity;
cols = vdev_raidz_get_logical_width(vdrz, txg);
ASSERT0(asize % (1 << ashift));
psize = (asize >> ashift);
psize -= nparity * DIV_ROUND_UP(psize, cols);
psize <<= ashift;
return (asize);
}
Read this, and try to understand what may the bug be. I will give you a hint; It is hiding in plain sight.
Now there is definitely the redclaration of the constant col which itself is dead code but there is something else in there.
First let's go through some technical talk (I learned a lot from the blog).
What is OpenZFS
OpenZFS is an open-source implementation of the ZFS file system , originally made by Sun Microsystems.
I think nowdays a ton of hobbyist trying to refine their system, comes across ZFS
ZFS is very similar to our traditional File Systems but with added features like snapshots, compression, RAID and mirroring.
Explain what each is out of the scope of this blog.
Getting to the grit
So in OpenZFS, any piece of data can be of 3 'sizes':
- Logical : Size of the file we see when checking the property of the file.
- Physical : Size of the data on the
diskafter compression or encryption and stuff are applied. - Allocated : Size that ZFS reserve for write operations (in virtual devices)
So to help the I/O pipelines, space allocator; every virtual device (vdev) uses some functions to convert the physical size to allocated size. (Why?, cause each vdev types store data differently or RAID-Z adds a parity overhead which makes allocated size larger than physical size)
So,
_asize_to_psize
: For a given allocated size, how much actual data can we fit in
_psize_to_asize
: For a given real data size, how much space should I allocate for it
So that was a lot of stuff, now try again. What would be the bug.
So it was the last line :
return (asize)
Please refer to the PR #17488
It was a small issue; it returned asize instead of psize. As the PR says,
tl;dr: a one-character fix ðŸ˜
Well bugs can happen anywhere at anytime, cause we are just humans sitting besides a keyboard. Seeing this code makes me really think, is the problem.... C?.
The compiler throws no warning or errors even tho psize is unused (-Wunused). You may need static analyzer, but from what I have learned about these code bases is that , such tools are used near to none (maybe cause it is slow??).
Note:
-Wunusedwon't work causepsizeis assigned a value but not read, it is enough for C to see it as 'used'
Bugs like this will look harmless for the user at first, then some wierd I/O bug will occur and before you know it, the whole data have been corrupted.
Maybe tools like Rust can be levereged in these spaces that can cross of bugs like these. Well the drama in the Linux Kernel is pretty... tasty. Yk, maintainers quits because of rust and stuff.
Since an example is provided in the original post, I am not gonna write any code for that. I believe in modern languages like Go, this may effect just like what happened in C.
Why I wrote all this
Cause, I think I have seen a lot of hate around langauges like Rust especially by the people 'maining' C. Thier rejecting of tools and sticking to purest form seems cynical. A good programmer doesn't need all this , they say.
Well , if you, the reader is someone having an opinion like It won't happen if you focused better or tried better, either you don't know sh*t about the stuff or you are a d*ck.
The bug as introduced by a very professional programmer and reviewed by another professional. But yk, they are humans too, stuff will happen.
Now don't get me wrong. I don't mean to re-write the whole X11 in Rust or anything , it is just impractical. I sometimes do think, if things were done in Rust or other languages in the same level, to understand where the original was weak. Well , atleast C dudes don't wear thigh high socks, right ?.