<p>In Rust, creating a raw pointer is allowed in safe code (i.e. outside of <code>unsafe</code> blocks), but then dereferencing it is unsafe:</p>
<pre class="lang-rs prettyprint-override"><code>let my_num_ptr: *const i32 = &amp;my_num;
let value: i32 = unsafe {*my_num};
</code></pre>
<p>Undefined behaviour would only occur in a program which both creates and incorrectly dereferences a raw pointer. So long as one of these operations is only allowed in an <code>unsafe</code> block, memory safety can be guaranteed for programs which have no <code>unsafe</code> blocks.</p>
<p>What if, instead of requiring an <code>unsafe</code> block for the dereference operation, it is instead required for the creation of the raw pointer? For example:</p>
<pre class="lang-rs prettyprint-override"><code>let i: const *u32 = unsafe {&amp;4};
let b = *i;
</code></pre>
<p>Or even:</p>
<pre class="lang-rs prettyprint-override"><code>let i: mut *u32 = unsafe {&amp;mut 8};
*i = 5;
</code></pre>
<p>In this hypothetical language, operations which create a raw pointer are unsafe (and must only occur in <code>unsafe</code> blocks), but dereferencing them is allowed in safe code, on the basis that it's the programmer's responsibility to make sure when creating the raw pointer that the pointed-to value will stay alive at that memory address for long enough. Is this sound? Would there be any advantages to doing it this way instead of the Rust way?</p>
<hr />
<p>Note that in Rust, a unsafe block does not mean that the entire program is unsafe, just that there are invariants the compiler can't check, so the programmer is responsible for them. If the programmer has made sure the rules are followed, it's perfectly safe.</p>
<p>In Rust currently, it is undefined behavior to dereference a null pointer, a unaligned pointer, or one that points to uninitialized memory. I'm suggesting that the undefined behavior happens when constructing such a pointer instead.</p>

<rust><memory-safety><pointers>
Can a language allow raw pointer dereferencing while preserving memory safety?
CC BY-SA 4.0
2023-06-16T20:47:45.637Z
I think this entirely depends on how you can get a raw pointer in the first place right? If the only way is `unsafe {&N }` (for some `N`) then of course there
rydwolf
CC BY-SA 4.0
2023-06-16T20:48:29.327Z
The only way to get raw pointers is manually constructing them, which is unsafe, or calling a foreign function which is already unsafe anyways. In both these cases the programmer is responsible for the invariants.
mousetail 'he-him'
CC BY-SA 4.0
2023-06-16T20:49:46.590Z
What semantic differences should there be between safe raw pointers, and normal shared/mutable references? The latter *are* Rust's way of guaranteeing safety, so it's not clear how what you want is different.
kaya3
CC BY-SA 4.0
2023-06-16T20:49:56.393Z
What are you asking then? If the only way to dereference a pointer still requires `unsafe` somewhere, then code without `unsafe` has to still be safe, right?
rydwolf
CC BY-SA 4.0
2023-06-16T20:50:33.447Z
No, this question is specifically if dereferencing raw pointers is safe if constructing them is unsafe
mousetail 'he-him'
CC BY-SA 4.0
2023-06-16T20:51:53.727Z
You'd use raw pointers for low level memory access of FFI
mousetail 'he-him'
CC BY-SA 4.0
2023-06-16T20:52:17.360Z
If constructing them is unsafe then the program is already unsafe. I'm not sure it's meaningful to ask if a different operation in an unsafe program is safe; a program having undefined behaviour means that all operations that program performs can do anything, even operations which can occur outside of `unsafe` blocks.
kaya3
CC BY-SA 4.0
2023-06-16T20:53:25.450Z
I don't understand how it would possibly be safe to dereference a pointer that's just a random `u32`...whether you call the creation or the dereferencing the `unsafe` part, the program's actual safety is identical...
rydwolf
CC BY-SA 4.0
2023-06-16T20:53:30.720Z
Unsafe blocks do not make the program unsafe, it just means the programing language does not check the invariant but the programmer is responsible. If the rules are checked then the rest of the program is safe
mousetail 'he-him'
CC BY-SA 4.0
2023-06-16T20:54:54.200Z
So it would be undefined behavior to construct a unaligned pointer or a pointer pointing to uninitialized memory, it's unsafe because the language won't check that for you. But if you do make sure as a programmer then the program is sound. This is how unsafe works in languages like rust
mousetail 'he-him'
CC BY-SA 4.0
2023-06-16T20:55:24.263Z
I think I see what you mean...so we sort of have three levels here, safe-safe, `unsafe`-safe (it's safe, but the compiler can't tell on its own), and unsafe-unsafe. And so you're asking, assuming we're in the second category, the pros and cons of requiring the `unsafe` block at creation vs. dereferencing?
rydwolf
CC BY-SA 4.0
2023-06-16T20:55:27.997Z
Unsafe blocks don't, no, but if the code in those blocks does indeed do something which violates memory safety then the program has undefined behaviour. A program with UB can do anything, it isn't obligated to execute normal operations that occur anywhere in the source code. Basically, UB is a property of a program that violates the compiler's assumptions, not a property of the individual parts of the code that cause that violation.
kaya3
CC BY-SA 4.0
2023-06-16T20:58:13.643Z
Upon re-reading the question, I think I now understand what you're asking. I'm going to make an edit which I hope clarifies your intent.
kaya3
CC BY-SA 4.0
2023-06-16T22:21:46.663Z
This is all very Rust-specific, right? Swift differentiates pointers to raw vs typed memory and it’s UB to cast the former to the latter in many cases — even if you only write to it and never read from it.
Bbrk24
<p>A language with a pointer type that disallows pointer arithmetic, and a &quot;fat pointer&quot; type which includes a base address, range, and offset, could uphold memory safety if the only &quot;unsafe&quot; operations were the formation of the above pointer types, in contexts where the address ranges associated with pointers would remain usable for the duration of the program.  Only address ranges that were not otherwise used by the application could have such pointers constructed to them, and any special functions that generated such pointers without being marked &quot;unsafe&quot; would need to ensure that they only returned pointers to storage meeting the aforementioned criteria, but many tasks that would otherwise require raw memory access could be accomplished even with such restricted semantics.  For example, a system which uses a memory-mapped display may be able to allow &quot;safe&quot; application code direct access to the display memory if it will be entitled to have exclusive access to that memory for its lifetime.</p>

CC BY-SA 4.0
2023-09-14T21:39:16.420Z
2023-09-14T21:39:16.420Z
supercat
<p>The secret is that most of the practical memory safety comes from bounds checking of arrays. So if you have that you get 80 % of the benefit while still allowing use of pointers. Odin is a good example of this in practice, <a href="http://odin-lang.org/" rel="nofollow noreferrer">http://odin-lang.org/</a>.</p>

CC BY-SA 4.0
2023-06-23T15:50:51.683Z
2023-06-23T15:50:51.683Z
Sam H. Smith
CC BY-SA 4.0
2023-06-23T15:53:24.840Z
This doesn't really answer the question, which is about whether memory safety can be fully ensured. This approach still allows dangling pointers.
rydwolf
CC BY-SA 4.0
2023-06-23T15:55:17.813Z
Those are only a problem if you have a long running application. And if you do have that, there are techniques that make it very easy to not make those mistakes. https://www.rfleury.com/p/untangling-lifetimes-the-arena-allocator
Sam H. Smith
CC BY-SA 4.0
2023-06-23T16:14:08.843Z
Why would danging programs only be an issue in long running programs? A bug can cause a dangling pointer instantly if the compiler doesn't prevent that
mousetail 'he-him'
CC BY-SA 4.0
2023-06-23T16:18:08.740Z
Because in a short running program you don't have to free. Try it. My self hosting compiler only invokes malloc and never free during it's entire runtime. You will not run out of memory, it is exceptionally hard to use even a gigabyte of ram.
Sam H. Smith
CC BY-SA 4.0
2023-06-23T16:21:42.613Z
You seem to be talking about memory leaks, not [dangling pointers](https://en.wikipedia.org/wiki/Dangling_pointer)
mousetail 'he-him'
CC BY-SA 4.0
2023-06-23T16:23:10.770Z
Note this entire question is about raw pointers, and you wouldn't use raw pointers for array indexing most of the time. Even in C you don't need to touch pointers to access arrays so discussing arrays is not really relevant to the question. In addition, like rust there would probably in addition to raw pointers be smart references for day to day safe use.
mousetail 'he-him'
CC BY-SA 4.0
2023-06-23T16:28:34.317Z
The pointer only ever becomes dangling because you freed memory. I'm saying raw pointers are not the source of memory bugs if you use the right technique. Almost all your trouble comes from going out of bounds on an array. C has this problem a lot because an array is just a pointer.
Sam H. Smith
CC BY-SA 4.0
2023-06-23T18:45:10.493Z
Out of bounds on arrays is very easy to check and not really the situation where you even need safety tools for checks.
mousetail 'he-him'
CC BY-SA 4.0
2023-06-23T19:54:33.240Z
Dangling pointers can still happen even without freeing heap memory if they're pointers to the stack. Similarly, this does nothing about wild pointers or null pointers (though they'll often segfault anyways on most systems).
Bbrk24
CC BY-SA 4.0
2023-06-24T03:43:52.630Z
"If you use the right technique" you don't know programmers? The only way to make programmers use the right techniques is either to make the compiler force them to (as Rust does) or make it illegal :P
Seggan
CC BY-SA 4.0
2023-06-24T06:32:10.547Z
Expecting programmers to "Using the right techniques" is entirely reasonable, C is very popular, but it doesn't answer the question since the question is about compiler soundness @Seggan-OnStrike
mousetail 'he-him'
CC BY-SA 4.0
2023-07-01T22:22:33.700Z
To LQP reviewers: This doesn't meet the criteria for either NAA (it does still attempt to answer the question) or VLQ.
rydwolf
<blockquote>
<p>In Rust currently, it is undefined behavior to dereference a null pointer, a unaligned pointer, or one that points to uninitialized memory.</p>
</blockquote>
<p>It's also undefined behaviour to.</p>
<ol>
<li>Access a block of memory after it has been deallocated.</li>
<li>Violate the aliasing/mutability rules.</li>
</ol>
<p>The first is bad, it essentially means that any pointer created based on a memory with non-static lifetime is a time-bomb.</p>
<p>The second though is arguably worse. Consider for example the following code.</p>
<pre><code>fn main() {
    unsafe {
        let mut a : String = &quot;hello world&quot;.to_owned();
        let b : * mut String = &amp;mut a;
        let c = (*b).as_str();
        *b = &quot;crash and burn baby&quot;.to_owned();
        print!(&quot;{}&quot;,c);
    }
}
</code></pre>
<p>Normally deriving a &amp;str from a String is safe. It's safe because not only is the String gauranteed to outlive the &amp;str, but the String is guaranteed to remain in a stable state for as long as the &amp;str exists.</p>
<p>In this case though, while the String does outlive the str it does not remain in a stable state for the lifetime of the str. When the String is reassigned to a new value, the str derived from it becomes a dangling reference!</p>
<p>Or to put it another way, no matter how much care is taken by the creator of a *mut String, the code derefencing that *mut String can invoke a serious violation of memory safety.</p>

CC BY-SA 4.0
2023-09-13T20:08:48.007Z
2023-09-13T20:08:48.007Z
Peter Green
<p>The trivial answer is yes, it's sound so long as the programmer keeps up their end of the bargain and doesn't create and then incorrectly dereference a raw pointer; and so long as one of those two operations is considered &quot;unsafe&quot; and must be put in an <code>unsafe</code> block, then programs with no <code>unsafe</code> blocks cannot violate memory safety in this way.</p>
<p>But I think that isn't a satisfactory answer to the question, because it doesn't address what burden is placed on the programmer to ensure that they use this pair of operations together safely.</p>
<blockquote>
<p>In Rust currently, it is undefined behavior to dereference a null pointer, a unaligned pointer, or one that points to uninitialized memory.</p>
</blockquote>
<p>I think the last point needs to be addressed. &quot;Uninitialised memory&quot; doesn't just mean memory which hasn't had a valid value written to it. For our purposes here, memory is also uninitialised if the allocation has been freed before the dereference occurs (i.e. use-after-free), even if it was initialised before it was freed.</p>
<p>I mention this not because it should be new information, but to highlight what I think is a significant problem with your idea. You can guarantee when the pointer is created that it is non-null, correctly-aligned and that the data it points to is initialised; but you can't guarantee at the creation-site that it will <em>stay</em> initialised for long enough. (Well, you could bind the lifetime of the allocation to the lexical scope it's allocated in, but that gives you the borrow checker, not raw pointers.)</p>
<p>To avoid use-after-free without a borrow checker, it is the programmer's responsibility to check <em>each time the pointer is dereferenced</em> that the dereference can occur only after initialisation and before freeing. That is, the programmer's burden is to ensure this at each dereference site. So it makes more sense to put the <code>unsafe</code> block around the dereference sites (as Rust does), because that's where your attention needs to be when you're checking that the pointer is being used correctly.</p>
<p>In contrast, if there is just one <code>unsafe</code> block where the pointer is created, then there is potentially a large amount of code outside of that block which also needs to be audited, but no &quot;warning signs&quot; telling you to look there.</p>

CC BY-SA 4.0
2023-06-16T21:25:40.870Z
2023-06-16T23:12:24.550Z
2023-06-16T23:12:24.550Z
kaya3
CC BY-SA 4.0
2023-09-26T21:32:29.350Z
I was approaching the question from the standpoint of raw pointers which can only be created to regions of address space which exist outside the "normal" semantics of the language, and which a program will be allowed to access for as long as it is running.  While this wouldn't accommodate all use cases associated with raw pointers, the possibility of "use after free" won't exist if there's no concept of "free".
supercat
# Can a language allow raw pointer dereferencing while preserving memory safety?
Posted by: username_0 on 2023-06-16
In Rust, creating a raw pointer is allowed in safe code (i.e. outside of unsafe blocks), but then dereferencing it is unsafe:
let my_num_ptr: *const i32 = &my_num;
let value: i32 = unsafe {*my_num};

Undefined behaviour would only occur in a program which both creates and incorrectly dereferences a raw pointer. So long as one of these operations is only allowed in an unsafe block, memory safety can be guaranteed for programs which have no unsafe blocks.
What if, instead of requiring an unsafe block for the dereference operation, it is instead required for the creation of the raw pointer? For example:
let i: const *u32 = unsafe {&4};
let b = *i;

Or even:
let i: mut *u32 = unsafe {&mut 8};
*i = 5;

In this hypothetical language, operations which create a raw pointer are unsafe (and must only occur in unsafe blocks), but dereferencing them is allowed in safe code, on the basis that it's the programmer's responsibility to make sure when creating the raw pointer that the pointed-to value will stay alive at that memory address for long enough. Is this sound? Would there be any advantages to doing it this way instead of the Rust way?

Note that in Rust, a unsafe block does not mean that the entire program is unsafe, just that there are invariants the compiler can't check, so the programmer is responsible for them. If the programmer has made sure the rules are followed, it's perfectly safe.
In Rust currently, it is undefined behavior to dereference a null pointer, a unaligned pointer, or one that points to uninitialized memory. I'm suggesting that the undefined behavior happens when constructing such a pointer instead.
**Tags:** rust memory-safety pointers
> **Comments:**
> **username_1**: I think this entirely depends on how you can get a raw pointer in the first place right? If the only way is `unsafe {&N }` (for some `N`) then of course there  
> **username_0**: The only way to get raw pointers is manually constructing them, which is unsafe, or calling a foreign function which is already unsafe anyways. In both these cases the programmer is responsible for the invariants.  
> **username_2**: What semantic differences should there be between safe raw pointers, and normal shared/mutable references? The latter *are* Rust's way of guaranteeing safety, so it's not clear how what you want is different.  
> **username_1**: What are you asking then? If the only way to dereference a pointer still requires `unsafe` somewhere, then code without `unsafe` has to still be safe, right?  
> **username_0**: No, this question is specifically if dereferencing raw pointers is safe if constructing them is unsafe  
> **username_0**: You'd use raw pointers for low level memory access of FFI  
> **username_2**: If constructing them is unsafe then the program is already unsafe. I'm not sure it's meaningful to ask if a different operation in an unsafe program is safe; a program having undefined behaviour means that all operations that program performs can do anything, even operations which can occur outside of `unsafe` blocks.  
> **username_1**: I don't understand how it would possibly be safe to dereference a pointer that's just a random `u32`...whether you call the creation or the dereferencing the `unsafe` part, the program's actual safety is identical...  
> **username_0**: Unsafe blocks do not make the program unsafe, it just means the programing language does not check the invariant but the programmer is responsible. If the rules are checked then the rest of the program is safe  
> **username_0**: So it would be undefined behavior to construct a unaligned pointer or a pointer pointing to uninitialized memory, it's unsafe because the language won't check that for you. But if you do make sure as a programmer then the program is sound. This is how unsafe works in languages like rust  
> **username_1**: I think I see what you mean...so we sort of have three levels here, safe-safe, `unsafe`-safe (it's safe, but the compiler can't tell on its own), and unsafe-unsafe. And so you're asking, assuming we're in the second category, the pros and cons of requiring the `unsafe` block at creation vs. dereferencing?  
> **username_2**: Unsafe blocks don't, no, but if the code in those blocks does indeed do something which violates memory safety then the program has undefined behaviour. A program with UB can do anything, it isn't obligated to execute normal operations that occur anywhere in the source code. Basically, UB is a property of a program that violates the compiler's assumptions, not a property of the individual parts of the code that cause that violation.  
> **username_2**: Upon re-reading the question, I think I now understand what you're asking. I'm going to make an edit which I hope clarifies your intent.  
> **username_3**: This is all very Rust-specific, right? Swift differentiates pointers to raw vs typed memory and it’s UB to cast the former to the latter in many cases — even if you only write to it and never read from it.  
---
## 4 answers
### Answer by username_2 on 2023-06-16. Score: 16
The trivial answer is yes, it's sound so long as the programmer keeps up their end of the bargain and doesn't create and then incorrectly dereference a raw pointer; and so long as one of those two operations is considered "unsafe" and must be put in an `unsafe` block, then programs with no `unsafe` blocks cannot violate memory safety in this way.

But I think that isn't a satisfactory answer to the question, because it doesn't address what burden is placed on the programmer to ensure that they use this pair of operations together safely.

> In Rust currently, it is undefined behavior to dereference a null pointer, a unaligned pointer, or one that points to uninitialized memory.

I think the last point needs to be addressed. "Uninitialised memory" doesn't just mean memory which hasn't had a valid value written to it. For our purposes here, memory is also uninitialised if the allocation has been freed before the dereference occurs (i.e. use-after-free), even if it was initialised before it was freed.

I mention this not because it should be new information, but to highlight what I think is a significant problem with your idea. You can guarantee when the pointer is created that it is non-null, correctly-aligned and that the data it points to is initialised; but you can't guarantee at the creation-site that it will *stay* initialised for long enough. (Well, you could bind the lifetime of the allocation to the lexical scope it's allocated in, but that gives you the borrow checker, not raw pointers.)

To avoid use-after-free without a borrow checker, it is the programmer's responsibility to check *each time the pointer is dereferenced* that the dereference can occur only after initialisation and before freeing. That is, the programmer's burden is to ensure this at each dereference site. So it makes more sense to put the `unsafe` block around the dereference sites (as Rust does), because that's where your attention needs to be when you're checking that the pointer is being used correctly.

In contrast, if there is just one `unsafe` block where the pointer is created, then there is potentially a large amount of code outside of that block which also needs to be audited, but no "warning signs" telling you to look there.
> **Comments:**
> **username_4** (2023-09-26): I was approaching the question from the standpoint of raw pointers which can only be created to regions of address space which exist outside the "normal" semantics of the language, and which a program will be allowed to access for as long as it is running.  While this wouldn't accommodate all use cases associated with raw pointers, the possibility of "use after free" won't exist if there's no concept of "free".  
---
### Answer by username_7 on 2023-09-13. Score: 3
In Rust currently, it is undefined behavior to dereference a null pointer, a unaligned pointer, or one that points to uninitialized memory.

It's also undefined behaviour to.

Access a block of memory after it has been deallocated.
Violate the aliasing/mutability rules.

The first is bad, it essentially means that any pointer created based on a memory with non-static lifetime is a time-bomb.
The second though is arguably worse. Consider for example the following code.
fn main() {
    unsafe {
        let mut a : String = "hello world".to_owned();
        let b : * mut String = &mut a;
        let c = (*b).as_str();
        *b = "crash and burn baby".to_owned();
        print!("{}",c);
    }
}

Normally deriving a &str from a String is safe. It's safe because not only is the String gauranteed to outlive the &str, but the String is guaranteed to remain in a stable state for as long as the &str exists.
In this case though, while the String does outlive the str it does not remain in a stable state for the lifetime of the str. When the String is reassigned to a new value, the str derived from it becomes a dangling reference!
Or to put it another way, no matter how much care is taken by the creator of a *mut String, the code derefencing that *mut String can invoke a serious violation of memory safety.
---
### Answer by username_4 on 2023-09-14. Score: 1
A language with a pointer type that disallows pointer arithmetic, and a "fat pointer" type which includes a base address, range, and offset, could uphold memory safety if the only "unsafe" operations were the formation of the above pointer types, in contexts where the address ranges associated with pointers would remain usable for the duration of the program. Only address ranges that were not otherwise used by the application could have such pointers constructed to them, and any special functions that generated such pointers without being marked "unsafe" would need to ensure that they only returned pointers to storage meeting the aforementioned criteria, but many tasks that would otherwise require raw memory access could be accomplished even with such restricted semantics. For example, a system which uses a memory-mapped display may be able to allow "safe" application code direct access to the display memory if it will be entitled to have exclusive access to that memory for its lifetime.
---
### Answer by username_5 on 2023-06-23. Score: -2
The secret is that most of the practical memory safety comes from bounds checking of arrays. So if you have that you get 80 % of the benefit while still allowing use of pointers. Odin is a good example of this in practice, <http://odin-lang.org/>.
> **Comments:**
> **username_1** (2023-06-23): This doesn't really answer the question, which is about whether memory safety can be fully ensured. This approach still allows dangling pointers.  
> **username_5** (2023-06-23): Those are only a problem if you have a long running application. And if you do have that, there are techniques that make it very easy to not make those mistakes. https://www.rfleury.com/p/untangling-lifetimes-the-arena-allocator  
> **username_0** (2023-06-23): Why would danging programs only be an issue in long running programs? A bug can cause a dangling pointer instantly if the compiler doesn't prevent that  
> **username_5** (2023-06-23): Because in a short running program you don't have to free. Try it. My self hosting compiler only invokes malloc and never free during it's entire runtime. You will not run out of memory, it is exceptionally hard to use even a gigabyte of ram.  
> **username_0** (2023-06-23): You seem to be talking about memory leaks, not [dangling pointers](https://en.wikipedia.org/wiki/Dangling_pointer)  
> **username_0** (2023-06-23): Note this entire question is about raw pointers, and you wouldn't use raw pointers for array indexing most of the time. Even in C you don't need to touch pointers to access arrays so discussing arrays is not really relevant to the question. In addition, like rust there would probably in addition to raw pointers be smart references for day to day safe use.  
> **username_5** (2023-06-23): The pointer only ever becomes dangling because you freed memory. I'm saying raw pointers are not the source of memory bugs if you use the right technique. Almost all your trouble comes from going out of bounds on an array. C has this problem a lot because an array is just a pointer.  
> **username_0** (2023-06-23): Out of bounds on arrays is very easy to check and not really the situation where you even need safety tools for checks.  
> **username_3** (2023-06-23): Dangling pointers can still happen even without freeing heap memory if they're pointers to the stack. Similarly, this does nothing about wild pointers or null pointers (though they'll often segfault anyways on most systems).  
> **username_6** (2023-06-24): "If you use the right technique" you don't know programmers? The only way to make programmers use the right techniques is either to make the compiler force them to (as Rust does) or make it illegal :P  
> **username_0** (2023-06-24): Expecting programmers to "Using the right techniques" is entirely reasonable, C is very popular, but it doesn't answer the question since the question is about compiler soundness @Seggan-OnStrike  
> **username_1** (2023-07-01): To LQP reviewers: This doesn't meet the criteria for either NAA (it does still attempt to answer the question) or VLQ.  
---
