Skip to content

Commit

Permalink
Fix GetMemoryChunkContext port (#1273)
Browse files Browse the repository at this point in the history
- The name was needlessly backwards-ish.
- `MemoryContextIsValid` performed a potentially-UB read.
  • Loading branch information
workingjubilee authored Aug 29, 2023
1 parent 1592952 commit d92a90b
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 47 deletions.
85 changes: 40 additions & 45 deletions pgrx-pg-sys/src/port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,63 +71,57 @@ pub const unsafe fn MAXALIGN(len: usize) -> usize {
/// # Panics
///
/// This function will panic if `pointer` is null, if it's not properly aligned, or if the memory
/// it points do doesn't have the a header that looks like a memory context pointer
/// it points to doesn't have a prefix that looks like a memory context pointer
#[allow(non_snake_case)]
pub unsafe fn GetMemoryContextChunk(pointer: *mut std::os::raw::c_void) -> pg_sys::MemoryContext {
#[cfg(any(
feature = "pg11",
feature = "pg12",
feature = "pg13",
feature = "pg14",
feature = "pg15"
))]
pub unsafe fn GetMemoryChunkContext(pointer: *mut std::os::raw::c_void) -> pg_sys::MemoryContext {
// Postgres versions <16 don't export the "GetMemoryChunkContext" function. It's a "static inline"
// function in `memutils.h`, so we port it to Rust right here
#[cfg(any(
feature = "pg11",
feature = "pg12",
feature = "pg13",
feature = "pg14",
feature = "pg15"
))]
{
/*
* Try to detect bogus pointers handed to us, poorly though we can.
* Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
* allocated chunk.
*/
assert!(!pointer.is_null());
assert_eq!(pointer, MAXALIGN(pointer as usize) as *mut ::std::os::raw::c_void);
/*
* Try to detect bogus pointers handed to us, poorly though we can.
* Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
* allocated chunk.
*/
assert!(!pointer.is_null());
assert_eq!(pointer, MAXALIGN(pointer as usize) as *mut ::std::os::raw::c_void);

/*
* OK, it's probably safe to look at the context.
*/
// context = *(MemoryContext *) (((char *) pointer) - sizeof(void *));
let context = unsafe {
// SAFETY: the caller has assured us that `pointer` points to palloc'd memory, which
// means it'll have this header before it
*(pointer
.cast::<::std::os::raw::c_char>()
.sub(std::mem::size_of::<*mut ::std::os::raw::c_void>())
.cast())
};
/*
* OK, it's probably safe to look at the context.
*/
// context = *(MemoryContext *) (((char *) pointer) - sizeof(void *));
let context = unsafe {
// SAFETY: the caller has assured us that `pointer` points to palloc'd memory, which
// means it'll have this header before it
*(pointer
.cast::<::std::os::raw::c_char>()
.sub(std::mem::size_of::<*mut ::std::os::raw::c_void>())
.cast())
};

assert!(MemoryContextIsValid(context));
assert!(MemoryContextIsValid(context));

context
}

// Postgres 16 does **and** it's implemented different, so we'll just call it now that we can
#[cfg(feature = "pg16")]
pg_sys::GetMemoryChunkContext(pointer)
context
}

/// Returns true if memory context is valid, as Postgres determines such a thing.
/// Returns true if memory context is tagged correctly according to Postgres.
///
/// # Safety
///
/// Caller must determine that the specified `context` pointer, if it's probably a [`MemoryContextData`]
/// pointer, really is. This function is a best effort, not a guarantee.
/// The caller must only attempt this on a pointer to a Node.
/// This may clarify if the pointee is correctly-initialized [`MemoryContextData`].
///
/// # Implementation Note
///
/// If Postgres adds more memory context types in the future, we need to do that here too.
#[allow(non_snake_case)]
#[inline(always)]
pub unsafe fn MemoryContextIsValid(context: *mut crate::MemoryContextData) -> bool {
pub unsafe fn MemoryContextIsValid(context: crate::MemoryContext) -> bool {
// #define MemoryContextIsValid(context) \
// ((context) != NULL && \
// (IsA((context), AllocSetContext) || \
Expand All @@ -136,11 +130,12 @@ pub unsafe fn MemoryContextIsValid(context: *mut crate::MemoryContextData) -> bo

!context.is_null()
&& unsafe {
// SAFETY: we just determined that context isn't null, so it's safe to `.as_ref()`
// and `.unwrap_unchecked()`
(*context).type_ == crate::NodeTag_T_AllocSetContext
|| (*context).type_ == crate::NodeTag_T_SlabContext
|| (*context).type_ == crate::NodeTag_T_GenerationContext
// SAFETY: we just determined the pointer isn't null, and
// the caller asserts that it is being used on a Node.
let tag = (*context.cast::<crate::Node>()).type_;
tag == crate::NodeTag_T_AllocSetContext
|| tag == crate::NodeTag_T_SlabContext
|| tag == crate::NodeTag_T_GenerationContext
}
}

Expand Down
4 changes: 2 additions & 2 deletions pgrx/src/memcxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,10 @@ impl PgMemoryContexts {
pub unsafe fn of(ptr: void_mut_ptr) -> Option<PgMemoryContexts> {
let parent = unsafe {
// (un)SAFETY: the caller assumes responsibility for ensuring the provided pointer is
// going to be accepted by Postgres `GetMemoryContextChunk`. Postgres will ERROR
// going to be accepted by Postgres `GetMemoryChunkContext`. Postgres will ERROR
// if its invariants aren't met, but who really knows when the ptr could have come
// come from anywhere
pg_sys::GetMemoryContextChunk(ptr)
pg_sys::GetMemoryChunkContext(ptr)
};
let memcxt = NonNull::new(parent)?;
Some(PgMemoryContexts::Of(OwnerMemoryContext { memcxt }))
Expand Down

0 comments on commit d92a90b

Please sign in to comment.