-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
pg_allocator feature causes segfault #37
Comments
I think, pg_allocator feature is perhaps unsound. Here is an example that demonstrates the issue which I was talking about (after your talk): use pg_extern_attr::pg_extern;
use pg_extend::pg_magic;
use std::sync::Mutex;
use lazy_static::lazy_static;
lazy_static! {
// Smuggle allocated memory in a lazy static!
static ref COUNTER: Mutex<Box<i32>> = Default::default();
}
pg_magic!(version: pg_sys::PG_VERSION_NUM);
#[pg_extern]
fn counter() -> i32 {
let mut guard = COUNTER.lock().unwrap();
**guard += 1;
**guard
} The issue here is that I'm sneaking allocated memory (in the form of a The very first invocation would allocate that The results I am getting:
Since the code is fully safe Rust not doing anything out of ordinary (shared state is not a good thing by itself, but this could be some cache), I think, this allocator is unsound for Rust code. Given that Rust code (in a theoretical vacuum) should be good about freeing the memory it uses, what are the benefits of using Postgres allocator? One option to make it safe(-ish), I think, would be to have a global counter in the The naive check, though, is prone to race conditions: if Rust code starts threads, counter might be zero when we check but code might still allocate just before it leaves to Postgres. Not sure if it is worth chasing, but maybe the solution would be to have function like |
I agree that there are issues. And yes, it's not threadsafe, though Postgres doesn't want you to use threads for anything, so it would be accurate to say spawning threads and using any postgres libraries would be considered unsafe. Not sure what we should do about that at-the-moment. Now that you bring up these points, I hadn't considered the fact that it's not guarded, and multiple threads could easily be stomping on each other. This could absolutely be the issue. A mutex should be used in this context. The only reason to use pg_allocator is to "play by the rules" of Postgres. I haven't built anything complex enough yet to discover if the default allocator would cause any issues, and given that Rust would be safely managing all of it it should be fine, except possibly where pointers are being handed back to postgres. Maybe we could come up with a strategy like you propose with arm/disarm at the FFI boundary? It might be complex to get that correct though. In the simple case, arm/disarm could be done when entering and leaving the FFI wrapper. And we'd have to find different points to do the same elsewhere... |
I might can shed some light on this. And if y'all already know this, then just please ignore... Postgres' (It looks to me like It's probably also worth noting that https://github.com/postgres/postgres/blob/master/src/backend/utils/mmgr/README In the case of @idubrov's example, the static Subsequent calls to For @bluejekyll's problem of things segfaulting (seemingly) before the allocator is even called is likely due to the same sort of thing. Rust likely allocated some memory, using Anyways, I wonder if it makes sense for the allocator function in this library to require a Also, it's worth noting that none of Postgres' memory allocation functions are thread-safe. It seems reasonable to me that even tho Postgres doesn't expect threads in the backend, a Rust extension could be threaded so long as the threads were not allocating memory through Postgres -- their memory usage would need to be completely isolated. It also seems reasonable to me to just not expose Anyways, I hope this is helpful. I've been doing Postgres extension/library development in C since PG v8.0, so I'm p familiar with how it works. However, I'm brand-spankin'-new to Rust. And also, I haven't actually used |
Thank you for this background. It’s great. For threading related things, I was considering serializing access to the allocator (if possible). Do you think this would be safe? |
So I just had a thought... Just take the approach Postgres does: If you want memory cleaned up whenever a IOW, don't set a GlobalAllocator for Rust, but instead expose all of Postgres' allocator functions for extension authors to use.
Nope. Even if Rust is properly serialized around EDIT: you also have Postgres' signal handlers to worry about, but I'm not exactly sure if they pause threads too? I actually ran into this with @zombodb's integration with And that might be part of the approach you should take too. I also think |
I guess maybe there's an exception here... If you can guarantee that control isn't returned to Postgres until the threads are all finished (ie, the Rust entry point for that function is joining on them), then yes, I think it would be safe (what about signal handler interruptions tho?), but man, it'd be terribly slow to synchronize memory allocation. I wouldn't actually want to use that. |
That’s an excellent point. I hadn’t considered it. The really big deal here is transferring data from one context allocated in Rust (malloc/free) to the Postgres context (managed in some form through the pg allocator). A minimum requirement we have is to figure out a way to allocate with pg for memory it needs to own, and then in Rust to never free memory owned by pg. it’s not an impossible task, but requires some thought on interfaces we’ll expose to do this. |
So it would need to be some sort of Copy thing where the memory is You could do some tricks with transaction callbacks to whole-sale free memory at the end of a transaction, but then you'd be holding Rust-allocated memory until the transaction finished, and not all transactions are single statements.
Yeah, I think that's just exposing all of Postgres' various memory management functions as top-level Rust functions. Basically, whatever's declared in Postgres' And then from there, if you can implement Postgres' |
Also, looks like Postgres has: /* Registration of memory context reset/delete callbacks */
extern void MemoryContextRegisterResetCallback(MemoryContext context,
MemoryContextCallback *cb); I've never used it, but the callback gets called as the comment says. Maybe that's a thing that'd be useful too? Have Rust install a callback whenever it notices it allocates memory in a |
Maybe you could do a thing where you, I dunno what' they're called in Rust, but annotate a function to indicate the MemoryContext you want it to use? #[memory_context(this = "CurrentMemoryContext")]
pub fn foo() -> i32 {
return 1;
} That's a bad example, but what I'm thinking is that somehow If unspecified, the default would be And then anything that Rust allocates that isn't directly called from Postgres (such as the COUNTER initialization in @idubrov's example) happens with Rust's default allocator? |
I discovered that with a new plugin I’m working on the pg_allocator feature is causing a segfault in the library when loaded and run in Postgres.
I haven’t got more details at-the-moment. But what’s strange is it looks like the segfault is occurring before the allocator is called (based on attempts to log in the GlobalAlloc functions).
The work around is to disable the pg_allocator feature.
The text was updated successfully, but these errors were encountered: