 All right, John Johansson from Canonical. And George is my partner in crime here. She's just gonna give support today, the machine that works. All right, so let's get moving. So traditional Unix model, we all know it. We have root and it has apps all together. We're just basically separating apps from user to user. And a lot of stuff is written with these design constraints in mind. And we really want to be moving more into what you see with Android and stuff where the user applications are confined, right? Some root applications are confined. Maybe everything is. And we've been doing this and with some success, some people don't like it so much. But we do have some issues when we talk about confinement like this, where sharing of data has been an issue between confined applications. You see a lot of, when we go with like a targeted policy, usually we're talking not scoping down on the application. It's at the user level still. And we want to get closer to what androids like, reduce the privilege, right? So this is partly for privacy because applications, you don't always trust what they're running. Linux, it's not so bad, but we still are running applications. We don't trust a whole lot. Some people still have to use proprietary tools. Then we have lots of tools in Linux to set up the confinement. We've got LSMs, SE Linux, App Armor, Tamoyo. SMAC, we have all the namespacing and container tools. Android tends to do a better job, iOS, because they actually have built APIs that are designed for separating these applications and sharing the data and dealing with it, right? Linux, not so much. And that's part of why we run into so many issues and people complaining. And people are used to doing things the way they're used to doing them, administering the way they're used to administering, and that's not always what you need or want for this type of model. The goal here is not complete isolation because if we do complete isolation, that's really not what the user's looking for, especially on a Linux model or a Linux model. They're used to having the data there and shared not manually having to say, yes, I wanna copy that over or do whatever, right? We need this to scale. And so for some people's systems, maybe you have everything locked down for other people's systems, that's just not possible because of the way they use their system. And we need to be able to kind of scale between those, right? So what we do doesn't necessarily have to apply to everything, but we want to be able to try to get to everything if we can. Linux being Linux, right? It's a ton of different APIs. We've got competing standards all over the place. We've got competing file pickers. We've got different ways to set up confinement with the namespaces. We've got different container systems. Every time you have a, people come out with standard or new two standards, then we get 10 standards, right? The relevant XKCD, right? Hey, we can make that up too. For this, for the purpose of this, we're gonna be using App Armor for our containment mostly. Maybe it's sometimes with Snap. So there's a little bit of other stuff going on as well. But it kind of is generic to all the container techniques. So ideally, we do what Andrew did and said, we've got a new API, whatever. We're gonna modify all the apps or force all the apps to use the API so that we can deal with the privacy, the data. What we've got right now is we have code directly querying and saying I wanna see all the files in this directory. And we don't wanna give the code acts, that program code access to all those files in the directory. What we want is we want this privilege separation, right? So we want the application to call out to something that has privilege that does that that we trust. And then it can feedback something to the application, right? We have something that can do this, right? We have APIs, we have portals now. That's a standard in Linux that we can do this with. But we're not wanting to modify the applications, right? Or we do want to, but we have to run applications that aren't modified unless you wanna do the work. So again, confinement sandboxing. Like I said, we have lots of ways to do this. It could be something that uses a launcher type technology. So it could be like a fire jail, a snap, a flat pack who set up a container, the confinement, and then launch the application into that container. Or it could be more a traditional LSM style like SE Linux or App Armor where you load a policy into the kernel and every application is assigned some kind of domain and it's just in it as part of the system launch. Like I said, for today, we're gonna be working mostly with App Armor around this and some of the things we've done. So and when we talk containers, because this can get confusing a bit sometimes, we're talking about it around the entire application, an application, say Chrome, can do its own little privilege separation and set up its own internal confinement like the renderer and Chrome is a separate process from the rest of the application. We're talking about setting up a confinement around all of this. We like that having that little sandbox in the application that it's nice to have, but it's irrelevant really to what we're talking. And since we're talking Privs up here, because of that, we're gonna take a little aside. Unprivileged username spaces, they've been brought up before. We have a real love-hate relationship with these things. From a security side point of view, we love getting rid of SUID, SGUID applications. The more we can get rid of those, the better. And username spaces let us do that. They also are really in use by several applications for their sandboxes that they set up, Chrome's browsers, one of them. And that really increases security, right? And so we love them for that, but... So pontoon, pontoon, 2022, four out of the five exploits that we fell to used, unprivileged username spaces as a part of their exploit chain setup. I didn't dig back into 2021 to get the exact figures on that one. We had several in 2021. We had several in 2020. We had several in 2019. It keeps popping back up again and again and again. And the issue isn't so much unprivileged username spaces itself, it's that it exposes kernel APIs that have bugs in them, and that's where the actual exploit lies, but they have to get to a point where they can get to them first. So this year we had NF tables, IOU ring. We've seen the CVEs for them so far. And we have only a really big SysControl hacker. Debian uses it, it's all or nothing, right? Hate that. So what we wanna get to, obviously, we wanna be able to control the applications, which ones get access to this thing. So since we're in Ubuntu, we're using App Armor to say which applications are trusted. It just makes sense that we're doing this. So we've grabbed the patch off of upstream that's been running around, that's been mentioned a few times, and we've pulled it into our kernel. And App Armor is gonna start enforcing confinement on applications on this. We're gonna carry this when it's not upstream, if we need to. Because it's so important because we just keep getting hit by it again and again and again. So the idea being is, if you have, if you're a privileged process, you get access to it, it's not a problem, right? It's only the unprivileged access that's the problem. And then you have to be confined. So if App Armor unconfined, doesn't usually have any restrictions on it, but it will, you won't be able to use unprivileged username spaces as a user unconfined. You'll have to have some kind of confinement around you and it can be really loose. You can set up an empty profile essentially that says allow everything almost everything and give it unprivileged username space access. And we'll have extra SIS control for App Armor controls on this so that it can be disabled. So user data, data and privacy. So like I said, we wanna reduce down what the data's being used and touched by applications. And we saw what we want to get to kinda idea. We wanna get to only application, only accessing what it needs to. It doesn't have to have access to all your data. And applications, they can talk to each other. They can talk to the disk. We need to be able to control the flow of that data. The confinement, that does it, but we need some way to actually tighten down how that data is, excuse me, used within the confinement to get this working well for users. And that's the big deal, big one, right? Is if users are complaining about the security of the system, because it gets in their way from doing things, forces them to jump through extra hoops, then they turn it off, right? We don't want this. If you all remember user account controls when it first came out with Windows, every action you get a window popping up, do you wanna allow access to this? Do you wanna allow access to that? Users, they just click yes. They just wanna get their things done. There's a few exceptions to that you could have. You see in Android, sometimes you'll get, you wanna allow geolocation or something like that. There's some things you just can't deal with without doing it, but generally speaking, you don't ever wanna ask the user a security question. You wanna minimize it. Again, we want the workflow where it just is natural. Like they're making the security decision just in their natural workflow. They don't know that they're making a security decision, right? That's the external file picker. We need to be able to dynamically update the data, right? This could be done like in an SE Linux type situation by updating the label on the file or the inode. You could put it on list, you can do whatever. It just needs to be extensible and you need to be somehow to be able to update this dynamically. There is a question about whether it's a long-term store versus a session data. Session data, if you're doing something storing it on an inode on disk is harder than something else, but not a big deal, you need some way to have it dynamic. Thankfully the LSMs, we're app armors at LSM, they're pretty good at the confinement data end of things. We're not so good right now, like I said, with the dynamic data end of things where we wanna just pass parts of data and maybe only have access to one file in your pictures or whatever, right? So what we need is we need a way to extract that user intent. So app rumor itself, like I said, we can do things, but we needed to improve what we're doing. One of the things we're doing here is we're adding a user conditional to our rules. We've kind of avoided this. It adds complexity and then you have to figure out whether you want a special internal user that's separate from the system, like SE Linux uses or whether you're gonna use the system ones, all that. We've kind of allowed it through using PAM and different domains, but really we need the flexibility of this on rules. It's not sufficient, but it makes policy a lot easier to write and we can make it more reactive to user, because we're gonna have to be updating. And unlike SE Linux, we're actually gonna go with the system user, but we have a way to abstract so you can make it abstract. So there's a layer there. It's through our variables, but we want to separate to some degree, application can always can stuff whatever data you want in there and say it has access to it, whatever labels. But we also want some kind of way to have a dynamic component that is passed around or can be passed around inherited and carried on it that's separate from what you normally think of as the confinement on this. That means we can isolate the application down more and throw around different data with it. This is much more dynamic. This is on a per exact type application level. It could be we're just handing around file objects, but it also could be we're actually handing around actual rules so there's extra information here for this confinement that is getting passed around. So how are we gonna go about extracting user intent? Well, there's no good way, right? So we're gonna go through a series of progressively nastier things that we've played around with and experimented with on what to do. Yes, we've sinned here, right? Well, there is maybe one good way. If you're a distro or building your own completely from scratch update your system libs, right? We can, you can have system libs that applications call and have them redirect to portals and stuff. GNOME has done this and, you know, so if you're using the basic GNOME APIs, you can get this automatically for a lot of them. They'll, you know, calling the picker will automatically go through the portal if it's there. Older GNOME libraries don't, but you can update them if you're a distro, right? That's the least nasty option. D-Bus, so D-Bus, lots of communication, especially in the desktop. It actually is interesting because it provides a lot of information in its header, right? So it provides more than just saying I wanna connect to somebody. It provides the path, the method. It gives you actually quite a bit of information that you can work around and play with. Now, on Ubuntu and with AppArmor, we use D-Bus, we label the ends and we have labels on our tasks and the D-Bus can pick this up. So our D-Bus mediation can use these labels so we can see what's on the other end. And one of the side effects of this is and SnapD takes advantage of this. So unlike FlapPack, we don't actually have to set up a separate bus for each confined application, whatever you wanna call them. We just use the labeling at the end point and we can use a single session bus because of the way our D-Bus has worked. So it's a small thing, you don't have to do it, but if you can do something like this, it can help with applications because we're FlapPack, again, FlapPack's just focusing on its own applications but you can have those confined launched applications but we also wanna be able to deal with applications that aren't that, right? And so having this, we can have them communicate whether they're regular, you know, Snap or FlapPack or whatever and still get the mediation and be able to deal with this passing of data that we come and so this helps having it on, having this extra infrastructure introspection mediation on the D-Bus helps us with this a little bit because we can pick out things like which method calls are going across and this looks a little bit like our file picker situation, right, where we have the privileged application, we have something separated, the D-Bus is separated and guess what, some of the APIs that go across D-Bus we can actually get pertinent information off of them and use that and feed that back into the mediation and it can reduce what you, you know, it can let you tighten down the data on the application, what it can access and not get in the way of the user and have it dynamic just like the picker and that does mean you have to have some extra extension to the D-Bus to delegate things back or call out other things but we're already on our D-Bus mediation calling up to the kernel and so this just provides an extra layer, this is an easier one to, you know, like what is the easiest things we can do without getting ugly, right? If you wanna get somewhat ugly, you can go in and sniff the packet data as it is and get more information. Like I said, it gets ugly, right? Environment variables. So at first this doesn't really seem like it's a big deal but it actually plays into it. Part of it is because LD preload, right? It allows us to inject code. Symbiote, the symbiote malware earlier this year, you know, sophisticated hiding of itself was using LD preload. From a confinement point of view, we actually want to often block and filter out these things but in this case, we actually sometimes wanna stick it in and control that it's there, right? Like it's a friend of a foe, right? It can be abused or we can abuse it ourselves and we can actually improve things using it. Of course, injecting code's ugly but sometimes that's the best solution you've got and that injected code can then replace the code you had so it'll go use the file picker. Gives us new functionality. We need to control it though, right? You can't just let this, if you're using it, you can't just let an application update it and then pass it on, right? So we have secure exec at secure in the kernel. We can set the secure exec flag and glubc. It will go down and it will clear all those environment variables. The problem is that's all, it does just that, it doesn't do any others and different applications may actually need them so like Python has its own set of environment variables and a few other things, other applications will have their own environment variables. They're not getting cleared here and there's ones we actually wanna clear, there's ones we want to set. The other thing being is secure exec, it was designed for suede, setuid, whatever and we're not always actually need all the constraints that secure exec's giving because we're often using this between applications with the same UID, right? So we wanna be able to be able to be a little finer control on that. It's easy if you have an application loader, right? You just set them and clear them when your application loads. It'll keep its own lists, clear what it needs, right? There's a little bit of a limitation on that but because if you have a situation where you have an application loader application like a SNAP who's doing its environment filtering, then it calls out to another application that doesn't have that, that application doesn't get the environment filtering and you could use that, you could exploit that to have the application if you compromised it to modify its own environment before it calls out, right? So that's a problem. So we wanna fix that limitation. That's the really big thing we don't need to fix. So how do we do it? We can extend App Armor to clear these out, right? Now, doing that in the kernel is not always the thing that people like the most if possible. Now, App Armor side, we have something that makes it a little bit better where we use a bounded state machine that's verified for mediation and it runs linear to the input, right? So we're always guaranteed on runtime, there's no loops, we're always guaranteed of completion, we're always guaranteed. So there's lots of things we can guarantee with this to make sure we're secure. Whether it's doing it in the kernels the right place, whatever, we can fiddle with it. We've been fiddling with it and playing with it. Where to, you know, what's the best way to do it? And it lets us, you know, we can do it so we can clear, set what environment variables we wanna clear for an application. We can have custom lists for different types of applications. We can set things and we can also just straight up deny apps if things are not what we expect. So if we see something change, when we have the environment we expect and it's just changed, we could just deny that instead, right? Is doing it in the kernel right place? Maybe not. We could actually redirect to a loader in user space. We'll talk about that and have a loader do it for us. What did I miss? Yeah, there we go. So application arguments. This is kinda similar to environment variables but even uglier in some ways. Again, it's nice that we have a bounded state machine that we can play with these with. And there's reasons you might wanna be looking at these for app armor and for our confinement. One of the things we wanna fix is, well, we'll go down one more. We wanna fix that we have this disconnect between launching something directly through bin format misc and from an interpreter, right? If we, on the interpreters, we could have some rules that would tell us which parameter to treat it as, then we could fix that. So that's one reason to reach in and make this more consistent for the user. And consistency is really important. That's one of the things that frustrates users. We get this actually all the time we get questions about this difference between going through bin format misc and not how the confinement works. Now, where application arguments are different than environment variables is we're not actually just doing a filtering or a setting. We're actually trying to extract information from them. And this is one of the things that's interesting and it's very hard, it's very application specific is how do you pull information out and say, well, this, you know, and this is one of the few things we can actually do with shell type programs is say, yes, the user typed in the shell. We know the shell's getting input from the user. So maybe we can't guarantee because maybe the shell's compromised. Here is the limited set, but we could, you know, are we gonna, do we wanna give it full access to what the shell has? Or are we gonna give this limited set with what's been typed in that's a subset of what the shell has access to. So we could pick those out and we can get this dynamic data. It won't work for everything and it's not perfect, right? Because the shell itself, if compromised, it could exact something and we can't distinguish that unless the shell somehow is providing us additional information. And we have no way of doing that, right? Well, not any good way. The forced launcher. So we already briefly talked about this around environment variables. One thing we can do is we can actually override in the kernel where things are what launcher they're gonna do, what loader they use in user space. We've played with this, it's interesting. And so what we can do is we can have a custom special loader and we can jump to that and it can set things up. So something it could set up if you wanted a separate debus session for an application and you don't have that loader that, you know, snapd or flatback has, you could force setting it up through something like this. You could do the environment filtering there instead of in the kernel. You could mess with our application arguments and you can do other really nasty things. Get ahead of myself. We have lots of things that are frowned upon in the kernel. Just wait. So we've talked about those ones. Additional sandbox set up. So you can do some things like setting up seccomp additional mounts, code injection, that's the fun one, right? And then you can re-exec into or just directly jump into the application through the special loader. So code injection. So binformat-misc is, I'm not binformat-misc, LD preload is a form of this, right? You can't always do LD preload because, well, you don't have the proper data, but you can actually go in and this is kind of fun if you've ever get a play with these type of things with malware and stuff. That's exactly what we're doing is we're looking at what does malware do? How feasible is this? It's not really feasible. It's not something you could ship at the distro level except for for select applications because you need patterns that you know are good that you can go in and overwrite or if they have a PLT of some kind that you could use, you can update that. At that point, you'd be able to use LD preload. So you can overwrite it to jump out to your own code routines that are gonna call out. The code has to, you have to know no one good code. You have to know the routines that you're replacing. So this is kind of ugly. And like I said, you can only use it selectively. I can't see it being something we could ever ship except for possibly in select applications if we had to support them. So can we do this without modifying injection into the code and the answer is, well, sort of maybe? So syscall address matching, right? So when you get a syscall, it comes into the kernel. You know the address that syscall came from, right? If you know where it came from and you know the code around it, so the pattern that sets it up, so the application is, you know it's coming from the application's file picker, for example. You know that the code around that hasn't been modified. So it has to be read only. And you know that there's no calls into that syscall indirectly that are malicious. And the only way we can really do that is through control flow integrity. Then you actually can decide to treat some stuff in the application itself without going privstep as higher priv because it's getting the pattern you want for the user. This isn't really feasible either because nothing's built with control flow integrity. But it's an interesting idea that you could reach into applications without having to directly modify them to set this up, right? And the reason you need that control flow integrity is because of Rop and Jop and things that can get if the application's compromised, could get to that syscall address and break your assumption that the code's unmodified and you know the chain it came through to get to it. And we do have some other things we've been messing with but we're not gonna discuss them today. So that's all I've got for today. Any questions? I believe that what you meant by code injection is really adding your code in the code existing for the application. Have you tried as well to add another library that is gonna be linked with a wrapper of yours? Okay, so by code injection, what you wanna do is you have the applications code and then you have your custom loader that you have the code patterns that you know you want or the addresses within that code. And when you load up that application, so we're not gonna modify the application on disk but when we recognize that application, we can go in and find those patterns, inject our code into them like a loader would go in and patch up the PLT and fix up all the relocations. At that point, you can actually also go into the code and you can modify it and the custom loader can then flip the switch, the bit protecting, tell the kernel that, okay, I want this code that I'm gonna write, I'm gonna make it writable first and then I'm gonna flip it back to read only before I actually start the application. And since we're doing it in our custom loader, normally with confinement, we would take and say, your code, you can't write to your code, right? We're gonna map this library but it's read only, you can't touch it. But if we're doing it in a trusted loader type situation, that trusted loader can have privilege to patch up the code in ways that you normally couldn't do and this is one of the reasons we don't wanna do it on disk is you'd have to make a copy of the library in advance because those libraries get shared. So you're kind of fudging around that and so we can inject the code in. So that's in a library but you can also inject it directly into the application code. So you take your custom bits that you want and you don't want those bits to be huge. What you want is you want those bits that you're injecting to be something that can call out to a portal or something that's a separate privileged application so that you can get the context you need for the, to pull stuff out of the application or the environment so that you can trust the user's input into the application that is not a malicious code that has been injected into the application at runtime from malware, separate from your launcher. Once we've actually patched up the code and then you set everything up to the application and jump back in, you're now back into regular confinement for that application. So anything else coming beyond that we expect with the confinement that that's a possibility at that point, right? That malware is happening and that's why we want the privilege separation. That's what, the whole point of that code is injecting is to get that proof step that the unmodified application that you have doesn't have. In Lesho again, you can do something like control flow integrity on the application and on those patterns then you don't actually have to modify it. If you know, have enough constraints on that, that is. Does that make sense? Thank you. I'm not sure maybe you've already played with this and I don't know, I'm just starting to think about the things you said, but so when you're talking about trusted loader and say restricting sis call invocations from certain areas that are trusted that you know to be immutable and whatnot. Have you tried prototyping some of this? If you have a trusted loader you could probably get a lot of the way there with the sec comp filter right now. I mean, I'm not aware of anybody actually using any sec comp filters that make use of the instruction pointer in there, but it is there. Right. If you had the trusted loader and you know how the code was laid out in memory you could restrict sis call invocation to certain segments that were loaded there. I'm just curious, have you played with that at all? I mean, does... A little bit. Okay, all right. So this is all, at this level this is all prototyping messing around, seeing what you can do, what's possible. How would you want to implement this and how would you want to bring it into a system? And sec comp is a decent way to play around and prototype with this. The issue with us with sec comp is if you're doing it at the sec comp layer how do you get that back into the other parts of your confinement, right? And that's the play. It's possible and I've thought about it but I haven't tried it to use like sec comps call back up into user space. So the user up call there. It's an interesting idea, I haven't messed with that. But yeah, filtering at the address there I've not actually pulled that on the app armor side and looked at the address. So I've been careful about saying, you know we're looking at the address and then we can use that, right? And we can, it's a matter of how you're gonna fit that into your confinement story, what you're setting up. Like I said, we've had to make some modifications to app armor to make our data more dynamic and work on that and play with that. We're still messing with how that's all gonna look. But yes, sec comps are useful for that. It's got the information. Yeah, I was just curious. Like I said, the capabilities there I'm just not aware of anybody that's using it. Perhaps someone is. I don't know if anybody using it beyond messing at that level, right? For that, it's interesting. Yeah. This appears like sort of a review of things we can do. It does. Yeah, do you have any specific threat model in mind for this array of things or what's your... What are you hoping to confine? What are we hoping to confine? We're hoping to be able to get closer to what Android's doing, right? So where every application is tightly confined to a point and without having to burden the user. Like I said, when you have file pickers and stuff, that's not bad, we can do that, right? It's a lot easier and we've been moving towards that. But we have applications where we don't have that. How can we deal with that and make it work in our confinement? And some of that, so some of it, you need this call picker, right? But even just doing things like making the data more dynamic, so you know app armor and the way that data is usually put in. And we can tighten things down a little bit, but then it starts saying, I'm not gonna give this application downloads, but the user has to copy from their downloads over separate from the application, right? And so if we can make things more dynamic, we can tighten things up even more. So you say if we launch events from Firefox, right? Even without going through the pickers and stuff, we can get down and say, well, when you call events, we're gonna let you delegate down to events, a subset of what you have access to. And we can better flow that way and tighter. And so we're not necessarily going to achieve everything with all of these. The goal is to try to get as close as we can. And we're playing and seeing how ugly things are, right? And some of these things are hideous. Like I said, you wouldn't wanna ship it unless you had a really compelling use case. But you wouldn't do it generically. Right, it sounds like, you know, Android started from we have nothing and we'll define some really narrow APIs and grow them as we need. And it sounds like this is more about trying to meet in the middle and basically create narrow APIs out of the existing native available libraries and things. It is, it's trying to figure out, and this is very much a search right now. We're trying to figure out, you know, can we get 50%, can we get 70%, where can we go, how feasible is some of this? And some of it, like I said, how do you do it? And you can't come up with it. There's no good way, right? Others, you know, I can get something close and it's not perfect. I'm passing in a subset of data, but it's not all the data. So I'm better than I was. And the user doesn't know, and it isn't any the wiser. And some of it is, yeah, we can actually do this. And we get 100% of the way there with something that's, you know, a little ugly, but is doable. Yeah, and the IP area in Sec Comp, like that was early required spec for some use case that ever used it. And no one, I too am aware of no one having ever used it, which is strange. But like two other things jumped to mind for this, like just having something available in the menu of things to try. I mean, I know you're aware of Landlock, like it has a more, it's currently a little narrow in what it can do, but you can do it from the application side, so that's nice. And the other one I've forgotten. Oh, the syscall interception, which is another interesting weird thing that you could do. You're like, I'm gonna emulate all the syscalls in this part of the code and in this part. Yes, yeah, you could emulate. I haven't played with emulating syscalls. You could, there's other things we can do that I didn't get into that are ugly for the LSM side of things that Colonel isn't set up for it right now that I've played with a little bit, like triggering on files. And so when an application goes and opens a file, triggering and changing your domain on that so that it affects what it can do from then on out. So think of it as tainting an application, right? So when it touches something else, it modifies it and then that information be carried along. The LSM isn't really set up for that right now. You can do it, but there's, how to put it, with LSM stacking the hooks, you really need a separation between permission hooks and hooks that do transitions because if you decide you're gonna transition in your LSM and another LSM decides to deny it, you get yourself into an odd state or potentially get yourself into an odd state. It could be that you don't, but it depends on what's going on, right? And so it makes it very hard from the LSM side. I've played with some of the triggering stuff, like I said, but when you have, like I said, LSM stacking, you're not guaranteed when you do your mediation that you're actually going to continue on. And that's one of the reasons it's not in here, it's something else, another topic. For like namespacing, we talked about earlier that we need more namespacing hooks, more than just permission hooks because with namespaces, there is very much a need at times to do some updates of your confinement, right? Your information you're carrying for your domain. And so we need some update hooks. And we do that already with exec, right? If you look at BPRM, we have the permission hook and then we have several update your state hooks as we go through. We need to do that a few other places. And that plays back into this as well when you talk about triggering on something. You made me think of two other things, like you could have your BPF LSM that would call the new app armor BPF helpers to transition state and like be able to generate stuff on the fly. And then I wonder on the stacking side for solving that, I wonder if we can like have a, you sort of make the chain of calls through the stack of LSMs. And on an error, we sort of come back up the chain and check each layer can recheck. It would be kind of interesting. It would be interesting. It would have to carry a temporary state that it's not committed. Oh yeah, no, I know. I touched it a lot. Yeah, it's mostly there. Yeah, it's just like, speaking of terrible ideas. Sorry. The second Cisco interception or user notification thing does let you do a few interesting things there. Like if you want to do prompting, technically we do have the semantics in place now for you to intercept open. That seems like a bad idea in many ways. But if you could totally have a policy in a partner that would normally prevent opening that would then have second still hit on that. You can then do a prompt or anything you want to see if the user is cool with opening that kind of Android style. Do you want to open this file now forever or whatever? Yep. And then what's the interceptor can do is then do it the open for on behalf of the process. And then there's the second return value thing that lets you send the FD across to the process and return that we work fine. It actually bypasses the LSM. So that's the part that's a bit scary. So with a straight up Cisco interception to just do that and do it in another privileged process, right? It doesn't fix the problem though. That what advantage do you get from doing that if you can't be guaranteed that that Cisco came as a user's interaction that you want to honor instead of code injected that's jumping through your Cisco that you're then mediating or not mediating intercepting. So for this, that in itself isn't necessarily where you want to go. But yes, the Cisco interception could be used to help figure out some state that you can then use. So like I said, if you have CFI then the Cisco interception can say I have it from this address and then I can work with it, right? Because then I know that that Cisco's all coming from that code that does that pattern that we have. And that's when it would work. Yep.