Hacker News new | ask | show | jobs
by fch42 509 days ago
Caution: I'm a C programmer "by history" and therefore I see "C Interfaces" as that and don't always / immediately view them through the rust lens of ownership...

Reading the article (nice troubleshooting story!), my summary, as a C programmer, is that the "C Interface" here "takes ownership". Given C cannot express this properly, a pointer is passed - and the called function "simply" makes the assumption that from hence-on, what was given to it will remain.

As "semantics" this (the need to pass an "owned" piece of data to a function) isn't unusual irrespective of the programming language. Just in case of Rust, this is explicit in the interface (if the func takes a non-ref arg, or a shared smart ref of sorts), while in C ... this can lead to errors of the observed kind. I haven't looked whether any of the sources or docs of libmodem say "this pointer must be either global/static or malloc'ed (and the caller shall not free it)".

A rust wrapper for this could / should possibly "leak a reference" here; Something that prevents the initialisation object from being dropped. yea, accepted, needs "nasty hacks" whether static lifetime, Pin, manual drops, explicit Arc leaks, ... possible though.

It'd be nice if libmodem were stricter about such ownership, agreed, and then a rust wrapper could take advantage. Takes time to evolve; is there a bug report / enhancement request out there for this in libmodem ?

2 comments

> Takes time to evolve; is there a bug report / enhancement request out there for this in libmodem ?

The end of the post says

> This would have been so simple to put in the docs. I've opened a ticket on their DevZone forum. As of writing they've still not updated the docs of the init function.

And they've replied

> Thank you for reporting this, it will be fixed in the next `libmodem` release by the end of the month.

C would benefit from a new keyword that allows a function to require a pointer argument be a non-stack object when passed from a function that returns. That would cover the most common cases where you accidentally pass in a temporary object that must persist.
The problem with this is that a pointer value may be passed through multiple functions or nontrivial control flow before reaching a function with this keyword. To effectively check if the pointer is in the stack or .rodata, it would need to do some kind of reverse data dependency analysis of all the possible places that pointer could come from to ensure none of the control flows could cause that pointer to be changed to a stack pointer.
Also ... the lifetime issue here doesn't preclude the use of stack-based variables. A "local" variable of main() is "as good as" a global/static to pretty much all parts of the program.
It seems like it wouldn't be hard to support a vendor-specific function parameters attribute like __attribute__((ptr_in_section(".data",".rodata"))__ or __attribute__((ptr_not_in_section(".stack"))
the stack (or stacks, for multithreaded processes) is not a "section" as far as executable file formats are concerned. It is amazingly hard at compile time to "know" that a specific pointer is on any stack.