Date: Fri, 22 Aug 2025 23:31:04 -0700
> On Aug 22, 2025, at 10:58 PM, organicoman <organicoman_at_[hidden]> wrote:
>
>
>
>> > Look at this example:
>> > ---------
>> > {
>> > int* p = new int(42);
>> > int* const& to_p = p; // as a guideline
>> > delete p; // if taken by reference an nullified
>> > *p; // this will be a guaranteed runtime error
>> > *to_p; // and all previous copies obey too
>>
>> You can’t do this last bit because you do not know where those references are.
>
> But if your original pointer outlives all these references, then it doesn't matter where they are.
Yes, that’s what makes solving use after free and lifetime in general hard.
> Tools like (&, const, volatile...etc), they have usage semantics behind them.
That’s irrelevant. Use after free bugs and errors in code, definitionally the semantics of the above aren’t relevant in terms of restricting the ability to make use after free or other errors.
> So if your API uses a reference, you are conveying the idea that your variable will outlive its reference,
UaF bugs are bugs due to code assuming a certain lifetime that is not actually true, your api can convey as much as it wants, that does not make the UaF go away,
> If your API uses const, you're conveying the idea that no change is guaranteed....and so forth.
That’s simply wrong. If your API takes a const the most that it’s conveying is that _it_ does not change the object (beyond any mutable members, const_cast<>), etc - it is not, and should not, a method to convey that no one is modifying the object. This is a misunderstanding of what const means.
Plenty of UaFs are via unexpected re-entrancy, state changes, etc - often in things like caches, which are often mutable so APIs through const references can still modify data.
>> > ---------
>> > On top of that, basically we need just to add a guideline that mandate:
>> > -All duplication of a pointer should be by reference if modifying, or const reference if not.
>>
>> Doing this would require allocating memory to hold the reference, and then having some way to manage the lifetime of that reference. At this point you have replaced raw pointers with handles, but handles have the same lifetime difficulties of the original pointer.
>>
> That's a smart pointers job.
If you can use a smart pointer, just use a smart pointer. Don’t try to make delete have magic behavior.
You keep presenting trivial straight line code, but that is not the primary source of UaF bugs.
References are not magic. They are literally just pointers, they do not provide any guarantees that a pointer does not have beyond making it UB for them to be null.
You’re trying to define the problem of resolving use after free vulnerabilities to just the case where it is already easily mitigated without language changes, once you move beyond trivial cases this does not provide any meaningful protections.
I’m really not sure what your operator delete is meant to be doing, but if we really wanted to do this we would just define operator delete as being required to write null to the object parameter if the parameter was an lvalue. That would also work for custom allocators as well, because it stops being something that every delete implementation would have to replicate.
—Oliver
>
>
>
>> > Look at this example:
>> > ---------
>> > {
>> > int* p = new int(42);
>> > int* const& to_p = p; // as a guideline
>> > delete p; // if taken by reference an nullified
>> > *p; // this will be a guaranteed runtime error
>> > *to_p; // and all previous copies obey too
>>
>> You can’t do this last bit because you do not know where those references are.
>
> But if your original pointer outlives all these references, then it doesn't matter where they are.
Yes, that’s what makes solving use after free and lifetime in general hard.
> Tools like (&, const, volatile...etc), they have usage semantics behind them.
That’s irrelevant. Use after free bugs and errors in code, definitionally the semantics of the above aren’t relevant in terms of restricting the ability to make use after free or other errors.
> So if your API uses a reference, you are conveying the idea that your variable will outlive its reference,
UaF bugs are bugs due to code assuming a certain lifetime that is not actually true, your api can convey as much as it wants, that does not make the UaF go away,
> If your API uses const, you're conveying the idea that no change is guaranteed....and so forth.
That’s simply wrong. If your API takes a const the most that it’s conveying is that _it_ does not change the object (beyond any mutable members, const_cast<>), etc - it is not, and should not, a method to convey that no one is modifying the object. This is a misunderstanding of what const means.
Plenty of UaFs are via unexpected re-entrancy, state changes, etc - often in things like caches, which are often mutable so APIs through const references can still modify data.
>> > ---------
>> > On top of that, basically we need just to add a guideline that mandate:
>> > -All duplication of a pointer should be by reference if modifying, or const reference if not.
>>
>> Doing this would require allocating memory to hold the reference, and then having some way to manage the lifetime of that reference. At this point you have replaced raw pointers with handles, but handles have the same lifetime difficulties of the original pointer.
>>
> That's a smart pointers job.
If you can use a smart pointer, just use a smart pointer. Don’t try to make delete have magic behavior.
You keep presenting trivial straight line code, but that is not the primary source of UaF bugs.
References are not magic. They are literally just pointers, they do not provide any guarantees that a pointer does not have beyond making it UB for them to be null.
You’re trying to define the problem of resolving use after free vulnerabilities to just the case where it is already easily mitigated without language changes, once you move beyond trivial cases this does not provide any meaningful protections.
I’m really not sure what your operator delete is meant to be doing, but if we really wanted to do this we would just define operator delete as being required to write null to the object parameter if the parameter was an lvalue. That would also work for custom allocators as well, because it stops being something that every delete implementation would have to replicate.
—Oliver
Received on 2025-08-23 06:31:35