* [pbs-devel] [PATCH v2 proxmox-backup 0/2] prevent potentially unaligned FixedIndexHeader reference
@ 2025-12-30 12:39 Robert Obkircher
2025-12-30 12:39 ` [pbs-devel] [PATCH v2 proxmox-backup 1/2] datastore: check for null pointer when allocating DynamicIndexHeader Robert Obkircher
2025-12-30 12:39 ` [pbs-devel] [PATCH v2 proxmox-backup 2/2] datastore: prevent potentially unaligned FixedIndexHeader reference Robert Obkircher
0 siblings, 2 replies; 6+ messages in thread
From: Robert Obkircher @ 2025-12-30 12:39 UTC (permalink / raw)
To: pbs-devel
Changes since [v1]:
- handle allocation failure
- reword commit message
[v1] https://lore.proxmox.com/pbs-devel/20251121143255.136721-1-r.obkircher@proxmox.com/
Robert Obkircher (2):
datastore: check for null pointer when allocating DynamicIndexHeader
datastore: prevent potentially unaligned FixedIndexHeader reference
pbs-datastore/src/dynamic_index.rs | 9 +++++++-
pbs-datastore/src/fixed_index.rs | 35 +++++++++++++++++++++++-------
2 files changed, 35 insertions(+), 9 deletions(-)
--
2.47.3
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
* [pbs-devel] [PATCH v2 proxmox-backup 1/2] datastore: check for null pointer when allocating DynamicIndexHeader
2025-12-30 12:39 [pbs-devel] [PATCH v2 proxmox-backup 0/2] prevent potentially unaligned FixedIndexHeader reference Robert Obkircher
@ 2025-12-30 12:39 ` Robert Obkircher
2026-01-07 13:10 ` Christian Ebner
2025-12-30 12:39 ` [pbs-devel] [PATCH v2 proxmox-backup 2/2] datastore: prevent potentially unaligned FixedIndexHeader reference Robert Obkircher
1 sibling, 1 reply; 6+ messages in thread
From: Robert Obkircher @ 2025-12-30 12:39 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Robert Obkircher <r.obkircher@proxmox.com>
---
pbs-datastore/src/dynamic_index.rs | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/pbs-datastore/src/dynamic_index.rs b/pbs-datastore/src/dynamic_index.rs
index ad49cdf3..12df78b1 100644
--- a/pbs-datastore/src/dynamic_index.rs
+++ b/pbs-datastore/src/dynamic_index.rs
@@ -41,13 +41,20 @@ proxmox_lang::static_assert_size!(DynamicIndexHeader, 4096);
impl DynamicIndexHeader {
/// Convenience method to allocate a zero-initialized header struct.
pub fn zeroed() -> Box<Self> {
+ let layout = std::alloc::Layout::new::<Self>();
unsafe {
- Box::from_raw(std::alloc::alloc_zeroed(std::alloc::Layout::new::<Self>()) as *mut Self)
+ let ptr = std::alloc::alloc_zeroed(layout) as *mut Self;
+ if ptr.is_null() {
+ std::alloc::handle_alloc_error(layout);
+ }
+ Box::from_raw(ptr)
}
}
pub fn as_bytes(&self) -> &[u8] {
unsafe {
+ // There can't be any uninitialized padding, because the fields
+ // take up all of the statically asserted total size.
std::slice::from_raw_parts(
self as *const Self as *const u8,
std::mem::size_of::<Self>(),
--
2.47.3
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
* [pbs-devel] [PATCH v2 proxmox-backup 2/2] datastore: prevent potentially unaligned FixedIndexHeader reference
2025-12-30 12:39 [pbs-devel] [PATCH v2 proxmox-backup 0/2] prevent potentially unaligned FixedIndexHeader reference Robert Obkircher
2025-12-30 12:39 ` [pbs-devel] [PATCH v2 proxmox-backup 1/2] datastore: check for null pointer when allocating DynamicIndexHeader Robert Obkircher
@ 2025-12-30 12:39 ` Robert Obkircher
1 sibling, 0 replies; 6+ messages in thread
From: Robert Obkircher @ 2025-12-30 12:39 UTC (permalink / raw)
To: pbs-devel
Continue to avoid the 4 KiB stack allocation in debug builds by
adopting the approach used for the DynamicIndexHeader.
This also avoid mutation through Vec::as_ptr, which is forbidden
according to its documentation.
Signed-off-by: Robert Obkircher <r.obkircher@proxmox.com>
---
pbs-datastore/src/fixed_index.rs | 35 ++++++++++++++++++++++++--------
1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/pbs-datastore/src/fixed_index.rs b/pbs-datastore/src/fixed_index.rs
index 6c3be2d4..7241d77f 100644
--- a/pbs-datastore/src/fixed_index.rs
+++ b/pbs-datastore/src/fixed_index.rs
@@ -29,6 +29,31 @@ pub struct FixedIndexHeader {
}
proxmox_lang::static_assert_size!(FixedIndexHeader, 4096);
+impl FixedIndexHeader {
+ /// Convenience method to allocate a zero-initialized header struct.
+ pub fn zeroed() -> Box<Self> {
+ let layout = std::alloc::Layout::new::<Self>();
+ unsafe {
+ let ptr = std::alloc::alloc_zeroed(layout) as *mut Self;
+ if ptr.is_null() {
+ std::alloc::handle_alloc_error(layout);
+ }
+ Box::from_raw(ptr)
+ }
+ }
+
+ pub fn as_bytes(&self) -> &[u8] {
+ unsafe {
+ // There can't be any uninitialized padding, because the fields
+ // take up all of the statically asserted total size.
+ std::slice::from_raw_parts(
+ self as *const Self as *const u8,
+ std::mem::size_of::<Self>(),
+ )
+ }
+ }
+}
+
// split image into fixed size chunks
pub struct FixedIndexReader {
@@ -237,7 +262,6 @@ impl Drop for FixedIndexWriter {
}
impl FixedIndexWriter {
- #[allow(clippy::cast_ptr_alignment)]
// Requires obtaining a shared chunk store lock beforehand
pub fn create(
store: Arc<ChunkStore>,
@@ -267,18 +291,13 @@ impl FixedIndexWriter {
let uuid = Uuid::generate();
- let buffer = vec![0u8; header_size];
- let header = unsafe { &mut *(buffer.as_ptr() as *mut FixedIndexHeader) };
-
+ let mut header = FixedIndexHeader::zeroed();
header.magic = file_formats::FIXED_SIZED_CHUNK_INDEX_1_0;
header.ctime = i64::to_le(ctime);
header.size = u64::to_le(size as u64);
header.chunk_size = u64::to_le(chunk_size as u64);
header.uuid = *uuid.as_bytes();
-
- header.index_csum = [0u8; 32];
-
- file.write_all(&buffer)?;
+ file.write_all(header.as_bytes())?;
let index_length = size.div_ceil(chunk_size);
let index_size = index_length * 32;
--
2.47.3
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [pbs-devel] [PATCH v2 proxmox-backup 1/2] datastore: check for null pointer when allocating DynamicIndexHeader
2025-12-30 12:39 ` [pbs-devel] [PATCH v2 proxmox-backup 1/2] datastore: check for null pointer when allocating DynamicIndexHeader Robert Obkircher
@ 2026-01-07 13:10 ` Christian Ebner
2026-01-07 14:29 ` Robert Obkircher
0 siblings, 1 reply; 6+ messages in thread
From: Christian Ebner @ 2026-01-07 13:10 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Robert Obkircher
Please provide a short commit message on why this is done.
As far as I see this now panics in handle_alloc_error() if either memory
is exhausted or the layout does not fit the allocator constraints, while
previously this failed on Box::from_raw() for the null pointer?
So maybe better to propagate the allocation error to the call site of
zeroed() in DynamicIndexWriter::create() and return it there as well?
On 12/30/25 1:42 PM, Robert Obkircher wrote:
> Signed-off-by: Robert Obkircher <r.obkircher@proxmox.com>
> ---
> pbs-datastore/src/dynamic_index.rs | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/pbs-datastore/src/dynamic_index.rs b/pbs-datastore/src/dynamic_index.rs
> index ad49cdf3..12df78b1 100644
> --- a/pbs-datastore/src/dynamic_index.rs
> +++ b/pbs-datastore/src/dynamic_index.rs
> @@ -41,13 +41,20 @@ proxmox_lang::static_assert_size!(DynamicIndexHeader, 4096);
> impl DynamicIndexHeader {
> /// Convenience method to allocate a zero-initialized header struct.
> pub fn zeroed() -> Box<Self> {
> + let layout = std::alloc::Layout::new::<Self>();
> unsafe {
> - Box::from_raw(std::alloc::alloc_zeroed(std::alloc::Layout::new::<Self>()) as *mut Self)
> + let ptr = std::alloc::alloc_zeroed(layout) as *mut Self;
> + if ptr.is_null() {
> + std::alloc::handle_alloc_error(layout);
> + }
> + Box::from_raw(ptr)
> }
> }
>
> pub fn as_bytes(&self) -> &[u8] {
> unsafe {
> + // There can't be any uninitialized padding, because the fields
> + // take up all of the statically asserted total size.
> std::slice::from_raw_parts(
> self as *const Self as *const u8,
> std::mem::size_of::<Self>(),
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [pbs-devel] [PATCH v2 proxmox-backup 1/2] datastore: check for null pointer when allocating DynamicIndexHeader
2026-01-07 13:10 ` Christian Ebner
@ 2026-01-07 14:29 ` Robert Obkircher
2026-01-07 14:57 ` Christian Ebner
0 siblings, 1 reply; 6+ messages in thread
From: Robert Obkircher @ 2026-01-07 14:29 UTC (permalink / raw)
To: Christian Ebner, Proxmox Backup Server development discussion
On 1/7/26 14:09, Christian Ebner wrote:
> Please provide a short commit message on why this is done.
Ok, I'll do that tomorrow, after I've finished the next version of "pipe
to stdin".
>
> As far as I see this now panics in handle_alloc_error() if either
> memory is exhausted or the layout does not fit the allocator
> constraints, while previously this failed on Box::from_raw() for the
> null pointer?
No, in the previous version this was undefined behavior, which is why I
assumed it should be ok to panic. For example, constructing a box from a
null can result in `Some(reference) == None` being true [1].
I've also seen the same issue in multiple other places. For example this
`uninitialized` function [2] is copy-pasted twice. That one is also
super dangerous, because if you accidentally read from it the compiler
will most likely delete your code [3]. Creating an uninitialized &mut
slice itself currently seems to be ok with a lot of asterisks though [4].
[1] https://godbolt.org/z/Ezfqbhqz3
[2] https://godbolt.org/z/ahx6vs8qz
[3] https://godbolt.org/z/4MePEKf79
[4] https://github.com/rust-lang/unsafe-code-guidelines/issues/346
>
>
> So maybe better to propagate the allocation error to the call site of
> zeroed() in DynamicIndexWriter::create() and return it there as well?
I don't think we can reasonably deal with that situation, because
returning an anyhow error most likely also requires allocation.
On a Linux system with overcommit enabled, allocations will probably
never fail anyway, unless the size is something huge like isize::MAX,
which is more likely to happen for an incorrectly sized Vec than for a Box.
>
> On 12/30/25 1:42 PM, Robert Obkircher wrote:
>> Signed-off-by: Robert Obkircher <r.obkircher@proxmox.com>
>> ---
>> pbs-datastore/src/dynamic_index.rs | 9 ++++++++-
>> 1 file changed, 8 insertions(+), 1 deletion(-)
>>
>> diff --git a/pbs-datastore/src/dynamic_index.rs
>> b/pbs-datastore/src/dynamic_index.rs
>> index ad49cdf3..12df78b1 100644
>> --- a/pbs-datastore/src/dynamic_index.rs
>> +++ b/pbs-datastore/src/dynamic_index.rs
>> @@ -41,13 +41,20 @@
>> proxmox_lang::static_assert_size!(DynamicIndexHeader, 4096);
>> impl DynamicIndexHeader {
>> /// Convenience method to allocate a zero-initialized header
>> struct.
>> pub fn zeroed() -> Box<Self> {
>> + let layout = std::alloc::Layout::new::<Self>();
>> unsafe {
>> -
>> Box::from_raw(std::alloc::alloc_zeroed(std::alloc::Layout::new::<Self>())
>> as *mut Self)
>> + let ptr = std::alloc::alloc_zeroed(layout) as *mut Self;
>> + if ptr.is_null() {
>> + std::alloc::handle_alloc_error(layout);
>> + }
>> + Box::from_raw(ptr)
>> }
>> }
>> pub fn as_bytes(&self) -> &[u8] {
>> unsafe {
>> + // There can't be any uninitialized padding, because the
>> fields
>> + // take up all of the statically asserted total size.
>> std::slice::from_raw_parts(
>> self as *const Self as *const u8,
>> std::mem::size_of::<Self>(),
>
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [pbs-devel] [PATCH v2 proxmox-backup 1/2] datastore: check for null pointer when allocating DynamicIndexHeader
2026-01-07 14:29 ` Robert Obkircher
@ 2026-01-07 14:57 ` Christian Ebner
0 siblings, 0 replies; 6+ messages in thread
From: Christian Ebner @ 2026-01-07 14:57 UTC (permalink / raw)
To: Robert Obkircher, Proxmox Backup Server development discussion
On 1/7/26 3:28 PM, Robert Obkircher wrote:
>
> On 1/7/26 14:09, Christian Ebner wrote:
>> Please provide a short commit message on why this is done.
> Ok, I'll do that tomorrow, after I've finished the next version of "pipe
> to stdin".
>
>>
>> As far as I see this now panics in handle_alloc_error() if either
>> memory is exhausted or the layout does not fit the allocator
>> constraints, while previously this failed on Box::from_raw() for the
>> null pointer?
Okay, thanks for clarification!
>
> No, in the previous version this was undefined behavior, which is why I
> assumed it should be ok to panic. For example, constructing a box from a
> null can result in `Some(reference) == None` being true [1].
>
> I've also seen the same issue in multiple other places. For example this
> `uninitialized` function [2] is copy-pasted twice. That one is also
> super dangerous, because if you accidentally read from it the compiler
> will most likely delete your code [3]. Creating an uninitialized &mut
> slice itself currently seems to be ok with a lot of asterisks though [4].
>
> [1] https://godbolt.org/z/Ezfqbhqz3
> [2] https://godbolt.org/z/ahx6vs8qz
> [3] https://godbolt.org/z/4MePEKf79
> [4] https://github.com/rust-lang/unsafe-code-guidelines/issues/346
>
>>
>>
>> So maybe better to propagate the allocation error to the call site of
>> zeroed() in DynamicIndexWriter::create() and return it there as well?
> I don't think we can reasonably deal with that situation, because
> returning an anyhow error most likely also requires allocation.
>
> On a Linux system with overcommit enabled, allocations will probably
> never fail anyway, unless the size is something huge like isize::MAX,
> which is more likely to happen for an incorrectly sized Vec than for a Box.
True, also allocation here is 4K only.
>
>>
>> On 12/30/25 1:42 PM, Robert Obkircher wrote:
>>> Signed-off-by: Robert Obkircher <r.obkircher@proxmox.com>
>>> ---
>>> pbs-datastore/src/dynamic_index.rs | 9 ++++++++-
>>> 1 file changed, 8 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/pbs-datastore/src/dynamic_index.rs b/pbs-datastore/src/
>>> dynamic_index.rs
>>> index ad49cdf3..12df78b1 100644
>>> --- a/pbs-datastore/src/dynamic_index.rs
>>> +++ b/pbs-datastore/src/dynamic_index.rs
>>> @@ -41,13 +41,20 @@ proxmox_lang::static_assert_size!
>>> (DynamicIndexHeader, 4096);
>>> impl DynamicIndexHeader {
>>> /// Convenience method to allocate a zero-initialized header
>>> struct.
>>> pub fn zeroed() -> Box<Self> {
>>> + let layout = std::alloc::Layout::new::<Self>();
>>> unsafe {
>>> -
>>> Box::from_raw(std::alloc::alloc_zeroed(std::alloc::Layout::new::<Self>()) as *mut Self)
>>> + let ptr = std::alloc::alloc_zeroed(layout) as *mut Self;
>>> + if ptr.is_null() {
>>> + std::alloc::handle_alloc_error(layout);
>>> + }
>>> + Box::from_raw(ptr)
>>> }
>>> }
>>> pub fn as_bytes(&self) -> &[u8] {
>>> unsafe {
>>> + // There can't be any uninitialized padding, because the
>>> fields
>>> + // take up all of the statically asserted total size.
>>> std::slice::from_raw_parts(
>>> self as *const Self as *const u8,
>>> std::mem::size_of::<Self>(),
>>
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-01-07 14:56 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-30 12:39 [pbs-devel] [PATCH v2 proxmox-backup 0/2] prevent potentially unaligned FixedIndexHeader reference Robert Obkircher
2025-12-30 12:39 ` [pbs-devel] [PATCH v2 proxmox-backup 1/2] datastore: check for null pointer when allocating DynamicIndexHeader Robert Obkircher
2026-01-07 13:10 ` Christian Ebner
2026-01-07 14:29 ` Robert Obkircher
2026-01-07 14:57 ` Christian Ebner
2025-12-30 12:39 ` [pbs-devel] [PATCH v2 proxmox-backup 2/2] datastore: prevent potentially unaligned FixedIndexHeader reference Robert Obkircher
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox