Date: Sat, 2 Mar 2024 21:54:42 +0000
I have to admit that at the beginning I was a bit skeptical in terms of what was being asked was either achievable or usable.
However, I did some further thinking on the issue and how to combine certain concepts to make something useful.
1.
And I have come up with the idea of stack swapping, i.e. mid execution a thread can swap its stack for another, given that this “another” could be a protected page locked to the current thread (i.e. only that thread can read it).
Assuming that restricting pages to specific threads is possible.
The idea being that access can never be changed. When the application needs to do something sensitive, it will swap its stack to this special protected one, do all of its cryptography there, return, swap the stack back, zero out the special stack before returning it to the system.
You could even use existing cryptographic libraries to keep it safe as long as they do everything on the stack, they wouldn’t be able to tell that they were running on a special stack. If they happen to require heap allocation then that will of course be leakable, but you can fix that by providing a special allocator where the pages are locked to the running thread.
As long as this part of the code is done correctly (which can be made to have a small testable surface), this kind of system would be invulnerable to overflow attacks. And even if you managed to get remote execution to work on some other part of the code you may not get that far, as there would be no facility available to unlock the page assigned to another thread, unless you can:
a) swap the running context of the thread that currently has privileged access to the memory at the right time. This is a higher bar to achieve.
b) get a root kit to gain the OS privileged access. At that point the entire system is screwed, and this type of protection wouldn’t make much of a difference.
As a bonus point, if you happen to leak this privileged memory, the system would be able to reclaim it back when the thread exits, or with a facility specially crafted to clear it.
This wouldn’t need to affect code generation of current applications given that you would need to explicitly opt-in by using special functions.
2.
Messing with code generation isn’t a bad idea, specially if we are dealing with open-source applications. I can envision an additional argument being passed to the compiler in order to randomize the layout of where certain functions or variables are relative to each other, or change other aspects of the resulting code to add additional fuzzing.
The side effect of which would be, even if someone managed to replicate the exact build environment, and figure out the exact version of the application that is running, and they have an exploit where they could target a specific place in memory, they wouldn’t have access to the exact build and would be much harder to figure out the code layout to make the exploit work.
Sure, this will have some impact on the predictability of the run time performance because of how the instruction appear in cache, but if what you are trying to do is protect data, predictable performance is not as high a priority.
3.
There’s always the lose point of how the cryptographic keys/credit card secrets end up in the application to begin with. As James hinted at, it seems like a bad idea that the user facing application that is subject to attacks and exploits from external malicious actors is also the application that has direct access to your passwords. If you have a separate application who’s only responsibility is to manage the secrets, and it can do it right, then the issue isn’t as much of a problem, this is not to say that this sort of memory protection isn’t useful, and protecting your one-time usable tokens isn’t worth doing, but perhaps may be less important if better security practices were adopted instead. There’s no magic solution that can save anyone if the developer just does “stupid shit”, and a minimum level of competence is required.
And I’m not sure if adopting better security standards is more productive.
In summary.
In any case it seems to me there is indeed a great deal of something that can actually be done, and definitely worth researching. But most of it involves either hardware or operating system design, this could benefit all programming languages that can be compiled into byte code, not just C++. The role of C++ would only be to standardize the API’s to make it available to the user. But these facilities will need to be created first outside of the C++ standard before the committee could do anything about it.
It is an interesting point of discussion; somebody should do research on this topic; maybe it will become standard practice in the future. But the C++ committee may not be the right venue.
Br,
From: Tiago Freire <tmiguelf_at_[hidden]>
Sent: Saturday, March 2, 2024 9:04 AM
To: Robin Rowe <robin.rowe_at_[hidden]>; sg14_at_[hidden]
Cc: undefined-behavior-study-group_at_[hidden]
Subject: Re: [SG14] Memory Safety and Page Protected Memory
I agree it doesn't have to be full proof in order to work.
And an answer could be all of the above.
Disconnected heap spaces
memory locks
memory scrubbers
safer designs to interact with sensitive data
they all do something, even if not perfect if at least can frustrate attacks to be statistically impractical for 50% of applications, we have still made things safer.
As long as it is understood that safer doesn't mean perfectly safe, I think we do have some points of actions that can be researched on and that can become reality.
However, I did some further thinking on the issue and how to combine certain concepts to make something useful.
1.
And I have come up with the idea of stack swapping, i.e. mid execution a thread can swap its stack for another, given that this “another” could be a protected page locked to the current thread (i.e. only that thread can read it).
Assuming that restricting pages to specific threads is possible.
The idea being that access can never be changed. When the application needs to do something sensitive, it will swap its stack to this special protected one, do all of its cryptography there, return, swap the stack back, zero out the special stack before returning it to the system.
You could even use existing cryptographic libraries to keep it safe as long as they do everything on the stack, they wouldn’t be able to tell that they were running on a special stack. If they happen to require heap allocation then that will of course be leakable, but you can fix that by providing a special allocator where the pages are locked to the running thread.
As long as this part of the code is done correctly (which can be made to have a small testable surface), this kind of system would be invulnerable to overflow attacks. And even if you managed to get remote execution to work on some other part of the code you may not get that far, as there would be no facility available to unlock the page assigned to another thread, unless you can:
a) swap the running context of the thread that currently has privileged access to the memory at the right time. This is a higher bar to achieve.
b) get a root kit to gain the OS privileged access. At that point the entire system is screwed, and this type of protection wouldn’t make much of a difference.
As a bonus point, if you happen to leak this privileged memory, the system would be able to reclaim it back when the thread exits, or with a facility specially crafted to clear it.
This wouldn’t need to affect code generation of current applications given that you would need to explicitly opt-in by using special functions.
2.
Messing with code generation isn’t a bad idea, specially if we are dealing with open-source applications. I can envision an additional argument being passed to the compiler in order to randomize the layout of where certain functions or variables are relative to each other, or change other aspects of the resulting code to add additional fuzzing.
The side effect of which would be, even if someone managed to replicate the exact build environment, and figure out the exact version of the application that is running, and they have an exploit where they could target a specific place in memory, they wouldn’t have access to the exact build and would be much harder to figure out the code layout to make the exploit work.
Sure, this will have some impact on the predictability of the run time performance because of how the instruction appear in cache, but if what you are trying to do is protect data, predictable performance is not as high a priority.
3.
There’s always the lose point of how the cryptographic keys/credit card secrets end up in the application to begin with. As James hinted at, it seems like a bad idea that the user facing application that is subject to attacks and exploits from external malicious actors is also the application that has direct access to your passwords. If you have a separate application who’s only responsibility is to manage the secrets, and it can do it right, then the issue isn’t as much of a problem, this is not to say that this sort of memory protection isn’t useful, and protecting your one-time usable tokens isn’t worth doing, but perhaps may be less important if better security practices were adopted instead. There’s no magic solution that can save anyone if the developer just does “stupid shit”, and a minimum level of competence is required.
And I’m not sure if adopting better security standards is more productive.
In summary.
In any case it seems to me there is indeed a great deal of something that can actually be done, and definitely worth researching. But most of it involves either hardware or operating system design, this could benefit all programming languages that can be compiled into byte code, not just C++. The role of C++ would only be to standardize the API’s to make it available to the user. But these facilities will need to be created first outside of the C++ standard before the committee could do anything about it.
It is an interesting point of discussion; somebody should do research on this topic; maybe it will become standard practice in the future. But the C++ committee may not be the right venue.
Br,
From: Tiago Freire <tmiguelf_at_[hidden]>
Sent: Saturday, March 2, 2024 9:04 AM
To: Robin Rowe <robin.rowe_at_[hidden]>; sg14_at_[hidden]
Cc: undefined-behavior-study-group_at_[hidden]
Subject: Re: [SG14] Memory Safety and Page Protected Memory
I agree it doesn't have to be full proof in order to work.
And an answer could be all of the above.
Disconnected heap spaces
memory locks
memory scrubbers
safer designs to interact with sensitive data
they all do something, even if not perfect if at least can frustrate attacks to be statistically impractical for 50% of applications, we have still made things safer.
As long as it is understood that safer doesn't mean perfectly safe, I think we do have some points of actions that can be researched on and that can become reality.
Received on 2024-03-02 21:54:46