C++ Logo


Advanced search

Re: Memory Safety and Page Protected Memory

From: Tiago Freire <tmiguelf_at_[hidden]>
Date: Sat, 2 Mar 2024 08:03:35 +0000
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.

From: Robin Rowe <robin.rowe_at_[hidden]>
Sent: Saturday, March 2, 2024 1:36:30 AM
To: Tiago Freire <tmiguelf_at_[hidden]>; sg14_at_[hidden] <sg14_at_[hidden]>
Cc: undefined-behavior-study-group_at_[hidden] <undefined-behavior-study-group_at_[hidden]>
Subject: Re: [SG14] Memory Safety and Page Protected Memory

On 3/1/2024 3:37 PM, Tiago Freire wrote:
> just because a piece of memory is readable it doesn't mean that it is
ALWAYS readable, and that is perhaps the key point to make this work.

A memory safe pointer's properties are like any other pointer in C. We
may declare a pointer const, but a cast can make it writable.

The idea of adding the feature of lockable memory, like some sort of
chmod setting for memory, is interesting. However, seems unnecessary. In
the case that the memory safe buffer is temporary, it's going away, so
nothing to lock. In the case we are keeping the memory safe buffer for
reuse later, we should zero the data in it. No need to lock it. If we
are to lock it for multi-threading, that would use an ordinary mutex.

> There's no such thing as a "special type of memory that cannot be
overrun into".

Yes, that's true. But in practice, any stack, static or heap pointer is
not in same memory region as a protected page. If we overrun one of our
conventional memory pointers, it's going to corrupt the stack, static or
heap. Overrunning one of our own conventional memory pointers is not
going to land in our protected memory buffer.

Yes, we could use a random number generator to Monte Carlo our way
through memory until we land by chance on a protected page we own, but
that seems too fantastic.

Yes, if we allocate two blocks of protected memory, those may be
adjacent. Then overrunning from our first safe buffer into our second
won't trap anything, because the overrun is into safe memory we also
own. However, recall that allocating page protected memory is expensive,
and the expected use case is a password buffer to store transient
sensitive data. Why are we allocating more than one password buffer?

All very interesting special cases above, and probably more, when
release implentation to test.

When I say "password buffer" I mean that in layman's terms. As you
correctly point out, cryptographic systems are not so simple.

Robin Rowe
Beverly Hills, California
*Chairman ISO WG21 SG14 C++ Banking and Financial Systems Subcommittee

On 3/1/2024 3:37 PM, Tiago Freire wrote:
>> Safe memory is special OS memory that is somewhere else, not the app's stack, not in the heap, not in static memory, at an address far away from all of the other pointers in the app. It is almost impossible that incrementing some other pointer in the app can overrun into a safe memory data buffer.
>> Not only that, a second requirement is that a pointer to a safe memory data buffer is impossible to overrun. A buffer overrun by a safe memory pointer is not undefined behaviour. Increment the safe memory pointer, then access off the end of its buffer, and the app will segfault or raise a signal every time, immediately. No guard bands, it just works that way at the OS level.
> 1. There's no such thing as a "special type of memory that cannot be overrun into". All memory is interfaced via an address, that address is a number, and any pointer to an address can be set to anything as long as it is arithmetically possible. This is not UB nor should it be, nor does it ever cause a segfault. And you will never be able to implement a solution that detects that you are accessing data out of bounds of the originally intended pointer, not now and not ever in the future, as long as the memory is accessible somewhere to the "application/thread" it is accessible.
> Segfault occurs only when you try to access it, and your hardware detects that the piece of memory is not assigned to what is currently running.
> For this to have any chance of success (and I do believe that the proposal does have its merits), this must be understood.
> 2. The second point that must be acknowledge is that this "specialized memory" must be readable. You create it for a reason, at some point the application must be able to use it, if it couldn't read it then it might just as well not be there thus making the problem mute.
> But just because a piece of memory is readable it doesn't mean that it is ALWAYS readable, and that is perhaps the key point to make this work.
> On hardware that support such form of memory segmentation, it is generally operating system that manages what memory is visible to different running applications, and it can only do this while running in an elevated privileged mode that regular user applications shouldn't have access too.
> Now, I'm not totally sure on the details, and correct me if I'm wrong, someone with more knowledge on how these systems work at the CPU level should be able to weigh in on this.
> In theory it should be possible to ask the operating system for a piece of memory that is locked away, then implement an interface where the application could ask the operating system to unlock it momentarily for reading, you do the tasks that you need to do, and then ask the operating system to lock it away again.
> And preferably "unlocking memory" should be done at the thread level (only the unlocking thread has access to it), as to avoid race conditions with other threads, and it will be the responsibility of a developer to make sure to only unlock it in "safe" parts of the code and ensure to always lock it back when no longer needed.
> Now this section of memory would still be vulnerable to buffer overrun attacks while it is unlocked, and there's nothing that would stop a remote execution attack from just opening the locks, but if other threads are unable to read it, and the memory is only unlocked in a very small well control piece of code, then you could effectively frustrate any buffer overflow attacks. It reduces the attack surface (which might just be enough in many cases) but it is not perfect.
> This will need operating system support, because only in privileged mode should you be able to operate the locks.
> Note protecting the password wouldn't be enough, passwords are rarely ever used directly, often they are just used with hashing to generate cryptographic keys, and I hope you are not just computing the hashes and cryptographic keys on the stack or regular memory because those are still accessible, and If I can't have access to your password having access to the keys work just as well. All of that will need to be protected, all of that very easy to do wrong,... yes doable, but also trivial to mess up without even realizing it.

Received on 2024-03-02 08:03:38