 My name is Case Cook, I'm here presenting with Gustavo. My name is Gustavo. We're talking about the progress on bounds checking in C, and most specifically how it applies to Linux kernel. If you want to follow along, the slides are there since we have some links in each page. Just quick agenda, what we're talking about, like our goal is memory safety. We're going to talk, look at, you know, C and it's missing bounds checking. Problems with the existing workarounds, where the current medgations are and why they lack coverage, and then what we've done to improve coverage, both with refactoring and annotation, and then the compiler work we've got, and some numbers at the end. So, goal is memory safety. One of the more tractable topics for memory safety is just bounds checking, getting spatial safety. A huge number of security flaws fall into this basic category, and a lot of it could be solved by the compiler. The problem is that the standard C language is just too ambiguous and lacks a way to describe a lot of these bounds. For simple arrays that are fixed size at compile time, these protections exist. There's all kinds of features we'll talk about later, where you can say, hey, if you've got an array of 16 bytes, cool. The compiler knows everything about it. It can do bounds checking at run time. It can do bounds checking at compile time. All sorts of fun things. But if it's not fixed, if it's not a fixed size at compile time, in the C language, you're basically just left with a pointer, which has no information about how large the region is you're pointing to, and that's just the basic type that's available. You have no choice. The reason this is important is because a really common data storage pattern is to have a header with a whole bunch of repeated elements afterwards. And in standard C, prior to C99, you had no way to associate the items that are following with the header. So you'd have to add up the allocation, and then you'd refer to the items that follow the header with a horrible cast to an addition off a pointer that's the wrong types. You're lying to the compiler because there isn't really a second header and you're just doing terrible, terrible things to work around the lack of this. You could have a pointer to the items, and this pointer would point to immediately after itself. So this is a kind of pointless exercise, but at least you now have an association inside that initial header structure that, yeah, you are in fact going to be followed by a whole list of items. So you're still doing the terrible casting. You're still doing this weird iteration, but at least there's an association. What's really wanted is a way to say, hey, I've got an array of these elements, and now the allocation is straightforward. You can iterate them, but what goes in the square braces? And this was the problem long ago. C didn't have any solution to this, so people lied about it. They said, I've got an element count. I've got an array that there's one element in it, because, of course, zero is not a valid size of an array in standard C. They'd say, I've got one element, and off they went, and continue to use many elements off the end, way beyond the single element. This had all kinds of problems that we're going to talk about in a second here. And in 1990, the GNU extension said, okay, we'll allow zero length, because at least we can use this as a notable extension. Standard C doesn't allow it, so it's a distinct feature that will give us the ability to solve some of the problems with the fake one element array, flexible array. But this also had all kinds of problems, because you're still lying to the compiler. You said, I have a size zero array. And you go, well, is it really? No, it's not. So 1990, we get C99 with an official flexible array member, which is just empty. So at least you're not lying to the compiler directly anymore, but you still have no idea how big this is. And then even worse than that is, since we've had 50 years of horrible hacks working around this, there are cases where people had a fixed size array at the end of some structure, and they said, you know what, this actually wasn't big enough. We're going to make it dynamically sized, but we're not actually going to refactor any code whatsoever. We're just going to pretend that that number that isn't one or zero is just totally a lie. So we're going to ignore it completely. You can talk about problems with existing workarounds. Yes. So I'm going to talk a little bit about the issues we have with one-element arrays and zero-layer arrays in particular. So the one problem with one-element arrays is, well, that one element. So whenever people need to make use of the size of the container structure, they need to be acutely aware at all times that they are also carrying with it the size of that one element of this array. So that's really problematic and because, well, it is going to force to the developers to always pay attention to any place where they need to compute either the size of the container structure or the size of the one element and see if they need to subtract from that size either one in the case of a variable that holds the total elements of our array as in the case of the line of code at the bottom or, well, any other case that the English this size, the size of this one element can be considered spurious, which is basically at all times except when we need to allocate a space in memory for our container structure and for our array. So here, well, we have an example of the declaration of a one-element array inside a stroke ancient. In this case, I decided to call this stroke ancient merely to illustrate that, well, this is the all way of doing this. So we don't want to do this anymore. So, yeah, you shouldn't do this anymore, too. And another problem with one-element arrays is that, well, you might know that we've been trying to enable array bounds for more than a couple of years now. And, well, if we enable array bounds, we are going to see a lot of false positives because, well, array bounds, what it checks is that we are not accessing our array outside or outside the boundaries of the array. If we declare a one-element array, well, the only valid index that we can access our array through is going to be zero. So that is not the case when we are using this type of array because the idea is that around time, the size of this array is going to be larger, right? So, yeah, chances are we are going to try to access this array through an index greater than zero. So, yeah, we've been fixing a lot of false positives trying to enable array bounds. And, well, this is an example of the type of warning that you're going to see if you're running to this issue. What's great about this is that, well, array bounds has been detecting some actual bugs. And, well, we've been fixing those. And, well, we're about to finally enable this option. So the problem with this is that in recent times some of the false positives that have been reported by the compiler are actual problems with the compiler code. So that's been delaying the progress in this work. Okay, so what are the problems with zero-length arrays? Well, they are slightly less problematic, but still something that, well, first they are not part of the C-standard, right? So another thing that we need to be aware of is that we have detected some code where developers were applying the size of operator to this type of array. The issue with that is that, well, we are always going to get zero as a result. And what we have seen is that they were trying to use the result of this operation, which is always zero, in other operations like multiplications, maybe they were trying to get the total size of the array around time. And, well, that's an issue, so we had to fix that. And, well, I have an example here of how people usually compute the size for the allocation of the container structure in the zero-length array around time. This is a common idiom. Okay, another problem with this type of arrays is that sometimes people can introduce problems like undefined behavior inadvertently. So in this case, I have here an example of a patch that added a new member to this structure after the declaration of a zero-length array. So the problem with this code is that around time this array is going to be used as a flexible array. So this is as an object of variable length, right? So the size around time is going to be much greater than zero, of course. I found this code, and, well, of course, here the easy fix is, well, we just move the zero-length array declaration again at the end of the structure, and that would be the fix. But if we also transform this array into a flexible array member, now the compiler is going to enforce this behavior. So the next time that someone comes and tries to add a new member and made the same mistake adding a member just after this array, well, now the compiler is going to complain. And that's always great because we are becoming the compiler our ally. And in these cases, well, this is something, I guess, we should always strive for. So, of course, this was the solution, right? So just move the array at the end of the structure and transform it into a flexible array member. So this was the kickoff for the flexible array transformations in the Kernel Self-Protection Project. This was the moment when we realized how important it was to transform this array. And just to mention that this both was introduced in 2011 and we fixed it in 2019. Here I have another example. This is interesting because what happens here is that the developer, originally they had in their code an array of 18 items, right? And then they wanted to transform this into an object of variable length around time. So they decided to transform this array into a one-element array. So they even added a comment, right, with the intention. The thing is that they also had this structure where they declared a couple of members of the type of the structure containing the one-element array. So the problem here is that if we... Well, if they tried to use this array as a variable length object around time, they are going to run into the same issue as the previous example. So they needed to move these declarations, at least this one, at the end of the structure. The others are fine because they are pointers. Another funny thing to notice here is that, well, the developer transformed a pointer to a type of this structure into a pointer to void. So that's obscure. So we really don't want to do that. But anyways. Okay, so as I mentioned, they forgot to move the declaration of this member at the end of this structure. And well, I was doing some work and I ran into this one-element array. And when I transformed this into a flexible array member, it was when I noticed this problem. So what I did was, well, I transformed the flexible array, the one-element array into a proper C99 flexible array member. And then, well, I moved the declaration of this member from the middle of the structure at the bottom, at the end of the structure. And with that, that issue is fixed. And well, just to mention, this bug was introduced in 2017 and was fixed in 2020. And well, this is another interesting case. Size of is going to return, of course, different results when we apply this operator to all these different variants of declaring a variable length object. So of course, if we apply size of to a one-element array, we are going to get the size of that one element. If we apply size of to a zero-length array, of course, we are going to get zero as a result. In the case of a flexible array member, a flexible array member is a variable of incomplete type. So in that case, size of is going to return error. So we need to be aware of these two. And this is another interesting case. This is an example of code. Here I'm showing code from BSD, where we have an array of concrete size in this socket address structure that is going to behave as a variable length object. So our array is going to be from 14 bytes all the way up to 2,255. So this is a couple of months ago, this was the reason why compilers couldn't reason properly about the sizes of trailing arrays. So this is something we are going to talk a little bit about later in the presentation. But this is an interesting case, too. So while there are mitigations that we can use in the compiler, we've got array bounds, which will tell us all kinds of things in compile time and fix them permanently. We've got the sanitizers that will do runtime bounds checking and says, hey, I've got a fixed size array. I don't know yet how much I'm going to be using or where I'm going to be indexing or how far I'm going to be copying. And at runtime, we can check the index or the offsets. And then similarly, the built-in, built-in object size, built-in dynamic object size provide similar checking for sort of fortified stuff like mem copy, string copy. But none of these work for trailing arrays, whether the size is specified or not. And so the problem here is if you've got an array in the middle, no problem. We're protected. Mitigation's worked. That's great. If you have something at the end, the compiler just completely steps away and says, I can't know. Code from 50 years ago has been doing insane things for so long. I cannot reason about this whatsoever. And then there's one where the size is specifically unknown. And it's also trailing, so it's not protected. But we want to have protection of something that actually the size is known. So in GCC 13 and Clang 16, strict flex arrays option was added, which basically says turn off all the insanity. Don't have any of the weird workarounds and other stuff. If you specify the size, it is using that size. Just done. And if you need a dynamically sized member at the end, it's got to be a C99 flexible array. So now we're back to, it's not about whether it's trailing. It's about whether the size is known. So if it's in the middle, it's at the end. If you have a specified size, you're done. Mitigation's actually work and apply. So now we've still got this problem of what do we do with unknown sized objects coming back around to the weakness in the C language itself. So the compiler feature that's being worked on, currently called element count. It is likely it'll get renamed to counted by or something, but the idea here is in most structures that have these kinds of trailing arrays, there is some other member within the structure that is used by the logic of the program to determine how big it is. You know, you're going to have a for loop that walks through every array element. You're going to have some counter that says what the maximum element is. Another way to specify, to hint to the compiler, hey, look, here is the actual size. You know, here's so many elements I have. And this actually gets us the point of being able to add the sanitizer protection, add this logic to the built-ins where we can actually check at runtime the size of this array. Okay, now let's see how we are, well, how we are transforming these arrays. Okay. I'm going to talk about the general case of one-element arrays and zero-link arrays. So, this example is, to me, is the most benevolent example I can show you. In this case, well, we have a stroke foo that contains one-element array of type, the element type of this array is going to be a stroke bar, right? And well, we have here, we are allocating memory through KM-alloc for the whole structure and a number of elements for our array. If you notice here, again, we are subtracting one from count, which is the variable that is going to hold the total number of elements in our array. And the reason for that, again, is because the size of the one element is already included when we apply size of to a star P, right? So, we have to compensate for that size. And well, we are going to do some things. We are going to copy some data into our array through main copy, and then we're going to iterate over our array, and that's it, right? Okay, how we transform this? Well, the simple solution is just we remove the one from the declaration of the one-element array, subtraction from count, and that's it. That is the solution. The sad thing about this is that maybe this is 1% of the cases that we have in the kernel. The real story is more complicated. We need to audit instances of size of when it's trying to get the size of the whole structure through, in this case, star P. We need to audit any instance of code when we find size of applied to the array. When we find size of applied to the type of the structure that contains the array. And of course, when we find instances of size of applied to the type of the element of the array. We also need to identify which is the variable that is going to hold the total number of items for our array, and try to look for instances where, well, we are subtracting one from this variable. Okay, but what if we don't find, in this case, count minus one, and we only find count. Is that intentional? Was that a bug? So we need to read a lot of code and try to determine what was the original intention. So, yeah, in some cases, we have found that the code, the original code already contains bugs, and we have to fix it. Also, what is quite interesting as well is when the type of our array is subsized one byte. As it could be, we declare our one element array of type U in 8 or char. In this case, people just subtract one from any of these size of operators. Yeah, we need to look for instances in the whole code, well, in the code that concerns our array, of course. Instances of minus one. So, to try to determine if that code is correct and if we need to remove that minus one, if that minus one is correct, so, yeah, it's a lot of fun. Lastly, we need to find instances of code where the structure that contains our one element array is a member of another structure, and we need to see if people is computing the size of that other structure and how they are computing that size and if we are missing, oh, well, if they are taking into account that they need to remove or to subtract the size of our one element array that is contained in this other structure and so on and so on. So, yeah, it becomes messy in reality. So, this is the reality of these transformations. We need to look for all these cases and try to make sure that we are actually doing what needs to be done. In the case of transforming zero-layer arrays into flexible array members, that's more straightforward. In this case, well, we can simply remove the zero from the declaration and that's basically it. However, we sometimes break user space and this is something that we are going to see. And, by the way, this is the script, the coaxial script we use to transform the vast majority of zero-layer arrays into proper C99 flexible array members. So, you see that it is a fairly simple script. We just identify the structure containing this array and remove the zero from the declaration and that's it. And, well, we also have the cases where we find these arrays in juniors. So, when we want to transform either a 1NM array or a zero-layer array into a flexible array member and that array happens to be in a union, the compiler is going to complain. There's no clear reason right now why that is the way it is, because, well, we could have a zero-layer array in a union and that's totally fine. Apparently, it's a definitional problem that probably is going to be fixed in the future. So, well, in this case, I have an example of one of these instances of code where we have a union, we have a zero-layer array and what we need to do in this case is just use a helper that we have for these cases. So, whenever we need to transform one of these arrays into a flexible array member we can use the declare flex array member. So, the declare flex array helper. This helper is useful for declaring flexible arrays in unions and also alone in structures, which is a case we have found. Okay, now, this is an example of how we've been doing all... how we started to do these transformations in UAPI. At the beginning, well, what we were doing was we were duplicating the whole structure into a union. So, this is because we cannot simply transform the one element array if it is in UAPI because user space is going to continue using that member, that array. So, we cannot just remove and modify UAPI in that way. What we need to do is we need to add a new member that is going to be a flexible array member and that member is going to be used by kernel space and the one element array is going to continue to be used by user space. So, this is an example of how we were doing things at the time. Now, with the help of the declare flex array helper, we don't need to duplicate the whole structure. We just need to include the one element array in a union together with the declaration of our flexible array member that is going to be used by kernel space. So, the code looks much better now. And, well, this is a case where someone from Android reported me that, well, I had broken user space through a chasing UAPI. So, what happened here is that the compiler is reporting that we have a flexible array member not at the end of a structure. As I have explained previously, when we were touching the case of undefined behavior due to the addition of a new member to this structure containing a zero-leg array, the zero-leg array was in the middle of the structure. So, if we modify that array and change it into a flex array member, now the compiler was going to emit this same warning. So, this is what was happening in user space. The code that introduced that bug in UAPI, well, is this. I send, at some point, I send a three-way patch to UAPI to replace all those zero-leg arrays. And, well, it happens that this code was being used in user space. And, in particular, in this structure. So, this code is user space code. So, here we have a couple of members declared in the middle of a structure. And the type of these members is the type of this structure in UAPI. So, yeah, now we have a couple of members of a type of a structure that contains a flexible array member just in the middle of a structure. So, that's wrong. We need to modify this. The problem with this is that there are a couple of members, not only one. If it were only one, well, we could just move that member to the end of the structure, and that would be the fix. But, in this case, I was honestly freaking out. I was like, oh, my God, I just broke user space, so Linux is going to kick me off out of the kernel community. But, yeah, the easy solution here is just to transform these into pointers. And that is what I proposed at the time, and that was actually the solution. So, yeah, I survived that. So, as we go through and get all these into actual flexible arrays, then we're in the position of actually being able to add more information about their runtime size. So, annotating things where we can about the size of allocations as possible, the alloc size, a hint to the compiler. Generally speaking, this is easy because you can just add the attribute to the end, specify which argument contains the size and bytes, and off you go. The tricky bit in the kernel, of course, is that we have lots and lots and lots of specialized allocators that do different things, that are wrappers around other things, or runtime fixed size allocations like the Kmemcache alloc. There's some problems in here, so it is somewhat limited in its use, and attributes get lost across inlines, which is a whole other bug in GCC. But it does catch real things. So, you know, if you perform an allocation, let's say here you've got your count on your alloc, and then you have a bug in whatever you're doing to access that array later, before this wouldn't get caught, and now with alloc size you can actually see, oh, the size information is actually available here on that indexing, so the sanitizer can catch this. Of course, if you return from this function, that size information goes away, but that's what we're hoping to fix later on here. And so internally, also, in addition to the indexing size information, you can use the built-in dynamic object size to find out the actual size of this thing due to the alloc size hinting in that function. So the next one, which will get us much, much wider coverage for runtime size, is adding the counted by attribute. So normally, this is, again, pretty easy. Structures have something that tracks it. Look, template and number templates. It's actually right there. It may require humans to look at it. So you just add, hey, this is counted by num templates, and off you go, and everywhere that you're using this structure in the kernel, now you actually have the runtime size available. So the built-ins will work, and the sanitizers will work. And you can actually find a bunch of these automatically. This is another Cox & L script for adding counted by. The pattern is you want to find the allocation where it's using the multiplier of the actual count followed by an assignment to a member of within that allocation. And then you can basically take that information, find where that structure was defined, and then add it in automatically. I've got a series that finds about 160 of these in the kernel with no special care whatsoever. It just finds this pattern. It's a really common pattern. There is some more compiler work that's sort of outstanding. Ongoing work in GCC and Clang. We still got this problem with dash W array bounds in GCC. GCC has a really interesting and effective way of tracking variable, like the range of values that may be assigned to a variable at compile time as it's going through as part of its analysis phase. So we can actually specify, hey, looking at all the paths to this one place where you index this array, I can see that you're going to be out of bounds under some condition. I'll throw warnings about that, which is really helpful. It's a very powerful tool. Unfortunately, it has bugs, and we've been fighting these bugs for quite some time now. With each release of the kernel, Gustavo and I have had all sorts of fixes landing. We're like, okay, we are warning clean. We are good to go, and then Linus says, oh, the development window is about to open. I will update to the latest Fedora with the latest GCC that has new false positives, and then I'll apply all these things and reject the fact that we can turn this on because now there's new false positives. He's done this two releases in a row, at least the second time he apologized to us. So I am foolishly hopeful that the last bug in GCC for this is a problem of jump-threading optimization, and once we get that fixed, hopefully again we can turn on dash W array bounds. We also have a, there's a problem with nested flexible array structures. Gustavo talked about this, if you're trying to count the one element array that's in a structure, and some other structure includes that structure, you end up with this nested series of structures that ultimately end in a flexible array. And GCC and Clang both, if you ask the built-ins, how big is the structure, like the internal structure, it says, I don't know, it ends in a dynamically sized thing, I can't know its size. But if you ask it, how big is the structure that contains this structure, it says, oh, it's exactly the size of those two structures without any, we'll pretend the flexible array doesn't exist, which gets confusing if you are trying to target something in a couple, in a sub-member of a structure. So changing the visibility of that is ongoing in GCC, we're gonna get that in Clang too, but there's some, since it's modifying the GNU extension of how that stuff gets processed, we're sort of waiting for it to be finalized in GCC before Clang follows that behavior specifically. There's also some questions about how the sanitizer works for bounds checking of arrays versus arbitrary object-sized checking, which is a kind of wider span of things to examine, but the latter, the object-sized sanitizer has had a lot of code gen issues, like making images really big and didn't seem to provide any coverage that dashed array bounds didn't already provide at runtime, like array bounds would do it at compile time, but then the object-sized sanitizer would do the same checks at runtime, which was unnecessary and redundant. So we removed it, but there's some question about whether or not we're gonna need to bring that back and fix it to work better with the coming annotations. And then of course, coordinating the counted by or element count or whatever it's gonna be called in the compiler. That one's ongoing, there's active coordination between Clang and GCC on that, so I'm hopeful that'll get resolved and published soon. Let's see, and I promised numbers. So this is kind of hand-wavy, but this was the last four years of refactoring to use flexible array members. This isn't gonna be a super precise graph, but it does capture the sort of the general sense of the work that needed to get done, and this starts back in like the 5.2 release where Gustavo was talking about that initial realization that, man, we're having bugs with the zero-size stuff, we gotta fix this. And then the work started to start the refactoring after going, you know, there's actually a lot of different things that depend on getting proper C99 flexible arrays used universally in the kernel with all the fake stuff gone. And there's sort of a bit of a flattened COVID area and then sort of tree-wide changes and then it's continuing. And we're basically close enough now that we're gonna be turning on the strict flex arrays things, which will change CodeGen and make a lot of other, make any remaining bugs pop out. We've had some long-standing stuff that we needed to fix first. We normally think of the kernel as being the ultimate upstream. Everyone else is downstream from the kernel distros and everything else, but actually the kernel is downstream for a couple of different projects like ACPI, CA, which has its own thing, and we just copy it directly into the kernel. But of course, it, being an older code base the kernel had many zero and one-length arrays scattered all throughout it. So getting that all cleaned up first was required before we can turn on the strict flex arrays because the kernel wouldn't boot because it's like, oh, it's a zero-sized array. I guess there's nothing else in this ACPI table. So the other metric that's not perfect but it's interesting to look at is looking at mem copy bounds checking so that using those built-ins as opposed to indexing of arrays and looking at that. So for an x8664 def config with fortify source enabled in 6.1 we had about 46% coverage. You know, you'd actually perform good bounds checking. And that was without alexize, without strict flex arrays. In 6.3 alexize is enabled, so we got about 54% coverage and I did a build with 6.4 RC1 and I forced strict flex arrays on and we got about 56% coverage. So my joke on this one is over the course of five months we've gained 10% coverage so in March of 2025 we'll have 100% coverage. Which is not true, I'm sure we'll hit 90% coverage very quickly and then spend years trying to get the last 10%. But I'm really hopeful that when we add support for the count-by attribute we're going to be able to convert a lot of these unknown, right? Which are the ones we can't mitigate. We convert unknown to dynamic and we have the actual bounds for all the mem copies, all the indexing and everything that we're doing. And I think that's it. Any questions? Thoughts, concerns? Yeah, maybe I missed it but I think I remember hearing that there are problems when you had a fixed array size which was a lie and the classic one of course is BSD SOC Adder in BSD SOC Adder games which of course must have been a really, really good dose of LDS at Berkeley. How does that get accommodated vis-a-vis UAPI and strict flex arrays equals three? Yeah. Thank you for your Star Trek reference. So what's interesting about that, the SOC Adder situation is that it was always a problem. Like making that lie created all kinds of chaos everywhere for every project that was using SOC Adder. And the kernel solution was actually to make another thing called like SOC Adder storage which is actually fully sized to the 255 max. And in most cases all the internals are already using that in most places and don't care and everything's fine and you have this weird interface to UAPI where everyone kind of winks and says yeah it's 14 bytes, sure sure and everything works because they've been fighting this for since SOC Adder decided to make that change. Inside the kernel though there's a passing of just a SOC Adder pointer and saying it's a SOC Adder pointer and people have to cast it to whatever protocol and figure out whatever it is. So that one has been painful to sort of deal with and a lot of those have been found by sort of turning on the sanitizer in warning only mode where we don't reject anything but we go what are you doing? Well this is bad but there was a large patch set that we landed for NFS a bunch of the NFS internals needed to be fixed to not trip the compilers checking, like it was fine it was doing all the right things it was just like it was lying to the compiler about what it was actually trying to dereference and you had to find all the places where you had to say no no it's not actually SOC Adder it's SOC Adder storage you do actually have 255 bytes dealing with that. So that's sort of been ongoing work which it's been a little painful to say the least there's also been issues with file systems XFS had a lot of similar one element stuff that went back and forth but SOC Adder was by far the worst of it because that was a very intentional we're just going to straight up lie to the compiler and move on and not refactor anything so huge tech debt there but for the most part that was one of the only serious offenders and what was so terrible is there's all these places where you just happen to have a structure that ended in an array and the compiler had to say I don't know about this this could be just like SOC Adder I can't touch it anyway I saw another hand what is the process going forward to ensure that new changes don't introduce these old styles is the like strict flex arrays option enough it's enough to make the kernel not boot and do other terrible things this is a place that I would like a little bit more help from the compilers on but I've sort of been picking my battles like can I get the feature that gets us the code gen we want first and then we can get warnings later so GCC has an option called dash W zero length arrays which will warn about finding a definition of a zero length array which would be handy because hey don't add those but it doesn't catch one element arrays which are usually unsensical there are strictly speaking legitimate uses of that but they're weird but we can't actually use that in the kernel because of all the UAPI that we can't change so we have all these definitions of zero length arrays even if the kernel itself doesn't access the array through that member name so what I really want is a dash W fake flex array access where it says oh you're about to do something with the thing that has a definition of zero one size and that'll keep it out there are some kind of hand wavy tools that we can use like check patch to say hey it looks like you're adding an array of zero or one size don't do that but the strict flex arrays will definitely cause chaos if you try to use it so I want to get those warnings but we don't have a good answer yet thanks I'd like to ask you which would be the faster and better to check all these sloppy code and to fix it versus change the safer languages like Rust I like to think that we're trying to peel away as many of the ambiguities of C as possible using less and less the C language that's dangerous while other people are working on getting Rust in from the other side my expectation is we end up meeting halfway where we have the best we can do in C and things start coming in on the Rust side but the way the kernel development process works it does not support kind of revolutionary change you can't just come out and go hey we've just replaced all the file systems with Rust it doesn't happen we have to incrementally make changes so I think as Rust is coming in at the periphery we still have to do our best effort to try and fix C where it is because it's going to be a long time before we get everything into a memory safe language but my thinking is they're not incompatible like we can use the C and the Rust and well it's good either way I think we're also at time awesome thank you