8 ms·
I read this as a damnation of POSIX filesystem semantics. File descriptors getting reused, insane locking semantics on close, allowing you to delete an open fil
by zvrba 4y ago
I read this as a damnation of POSIX filesystem semantics. File descriptors getting reused, insane locking semantics on close, allowing you to delete an open file and providing no means to prevent it, fork causing problems, unspecified behavior of sync() (is it barrier or "full" sync).
Though: Multiple copies of SQLite linked into the same application. Weird and rare scenario, but why not keep the global list of open sqlite files in a global shared memory segment?
- kevingadd 4y agoMy guess is the shm to detect multiple copies linked in would probably be a lot of complexity for the sole purpose of trying to compensate for a really bad practice.
- psyclobe 4y agoYeah global shm state is a bitch you have kinda a ownerless resource that must be managed somehow cooperatively. This adds global mutexes to the equation and quirky concepts on initialization and abandonment of the shared state. Usually none of these issues are dealt with in the shared memory sdk you’ll be using, so you gotta model it with mental experiments just like you would a lock free implementation in a threaded process. Simple no, doable yea, error prone you betcha.
- zvrba 4y agoYeah, I agree.
- CGamesPlay 4y agoHow would the multiple copies find the global memory segment? In theory each copy could create such a segment and check that all handles received by APIs live in it, but I can’t see how you could convince multiple versions to get a handle to the same shared segment (well, without a file handle).
- zvrba 4y ago> How would the multiple copies find the global memory segment? They would all use the same reserved "name". It's possible to atomically create or open a SHM segment with a given "name". Then have a header at the start of the segment with critical metadata like signature, version, etc. Barf unconditionally if the metadata is found to be invalid (i.e., some other process deliberately used the name to sabotage sqlite [1]), or, in addition, have an option to not use SHM. [1] The situation is not different from some process sabotaging another by deleting or corrupting well-known files.
- CGamesPlay 4y agoI might be misunderstanding something, but doesn’t that mean that only one SQLite library could be loaded per system? That hardly seems like an acceptable compromise for an embedded software.
- SQLite 4y agoYou misunderstand. It means that a single application should not use two or more copies of the SQLite library to open the same database file. As a practical matter, you really have to work hard to get an application to use two different copies of SQLite at the same time - all the while avoiding symbol collisions on link. You can do it, but it takes some work. And then on top of that your application has to decide to open two or more connections to the same database file, using different copies of SQLite in each case.
- mst 4y agoYeah, managing to do that is ... almost impressive. The more common failure mode I've seen is a library bundling and inlining an old version of a database API in a way that means it overrides the shared object I was expecting my code to link to, and alarums and excursions resulting thereby. (I don't imagine SQLite would -break- in that situation, but I doubt my application code would be any less unhappy than in the cases of that problem I've encountered in the wild)
- 4y ago
- gigatexal 4y agoEhh. I kinda like being able to update the browser binary while it’s in use and and in memory and then only get the new version when I restart the browser.
- ectopod 4y agoWhat I get is a crash when I open a new tab because the process launched to manage it is using the newly installed binary, which isn't compatible with the old binary that's running. Similar problems occur if you do in-place upgrades of software using dynamic libraries.
- afiori 4y agoit is fine to allow for such a feature, it is also nice to allow protecting against this possibility
- bayindirh 4y agoWhy everything which doesn't work as someone expected is called "damned"? These features have been there for decades and we know what to do, what not to do, and how to exploit these features. I personally like how POSIX works, and how well documented its operation and failure modes. I love systems which don't prevent foot guns, and go bang spectacularly when I foot gun myself. It's also ironic how we don't get to like systems which do limits us in severe ways, and then get angry to a system which doesn't limit us the same way. Fun times.
- ynik 4y agoWe know what to do, but no single developer is in a position to do it -- POSIX file locking requires global coordination between all developers of code running inside a single process. This is fundamentally incompatible with the use of libraries. Observe that there is no way SQLite could fix this bug, the SQLite developers instead have to shove off the responsibility to everyone one using the SQLite library. Add a few more layers of libraries where this requirement isn't documented as clearly, and this is basically guaranteed to go wrong somewhere.
- zvrba 4y ago> Why everything which doesn't work as someone expected is called "damned"? Because POSIX doesn't provide features that are necessary for reliability. Workarounds, if they exist, are highly platform-specific. That I'd say is damnation of the standard. For example, how do you, within POSIX, ensure that a write to the storage media is "durable"? People argue about what combination of various "sync" calls and open flags you need to achieve this. (Disregard the case of hardware cheating -- nothing can be done about that.) How do you make an "owning" lock on a file: a kind of that you and only you (the owner) can remove? Why is a lock applied per for file descriptor and not the file object? What is the workaround here? In general, POSIX + threads = damnation because there's too much per-process global state, which was kind of OK "before MT". It's clear that the standard was written before multithreading was even foreseen to be the norm, and it contains many SNAFUs wrt threads, not just files. (Signals and fork are the first to come to mind.) Oh, yeah, don't mention Linux. It has so many extensions and additions to POSIX to alleviate POSIX messups that it's not even funny.
- hnarn 4y ago> allowing you to delete an open file and providing no means to prevent it What exactly do you mean by “providing no means to prevent it”?
- zvrba 4y agoLike Windows file share mode. Mandatory, "hard" lock on a file. The OS doesn't let you delete it if another process holds it open. (You can forbid any of 3: reading, writing, deleting by another process. It is completely orthogonal to the ACL permissions system.)
- tinus_hn 4y agoAnd it’s the reason you need to reboot all the time.
- zvrba 4y ago??? All the time? I reboot ca once a month, for windows update.
- BenjiWiebe 4y agoYou can update Linux without rebooting, except for the kernel (and even then there's options). Rebooting once a month is way more often than some of my Linux systems.
- zvrba 4y agoEh. What about updated libc.so or libssl.so? If daemons using them do not get restarted, they keep using the old version. If they do get restarted, well, then, it's just as if you rebooted the machine. Somewhat quicker, but in effect it's a reboot.
- oauea 4y ago> but in effect it's a reboot no. restarting some daemons one by one when it's convenient is wildly different from taking down the entire system
- Pxtl 4y agoWindows has the reverse problem, where you are blocked to delete an in-use file, but it's buggy as hell and very opaque as to what is holding that file.
- zvrba 4y agoThe windows feature is not buggy, the process that created the file potentially is as the creating process chooses the sharing mode. Except for executable images (EXE/DLL): they're always locked on the filesystem as long as any running program is referencing the image. That's by design: designers didn't want "invisibly" used space on disk. For what happens when you do that on Linux: the file entry gets deleted, but disk space doesn't get deallocated until the last program using the file has exited. If you replace the file, the program will still be referencing the old file (now unreferencable by any name/path, except possibly through /proc/../fd).
- Pxtl 4y agoI've dealt with far too many cases of crashed programs retaining their locks on files to call it anything but buggy. And Windows offers no UI for finding out which process is holding the file handle.
- zvrba 4y agoI think your diagnosis is wrong. When the program exits, in whatever way, all resources/locks/etc are cleaned up. (Hey, the OS is also nice enough to tell waiters about abandonend mutexes.) There's no built-in GUI, but there's Process Explorer and other sysinternals utilities that you can use to find out what is keeping the file open.
- layer8 4y agoWindows comes with Resource Monitor which can be used for that purpose: https://superuser.com/a/643312/62 https://superuser.com/a/643312/62
- asveikau 4y ago> File descriptors getting reused I don't know how you could avoid this, and it has me wondering if you understand the issue. This is basically the file descriptor version of a use-after-free bug. I'm not one of those "rust fixes everything" people, so I don't answer this about just anything, but I think this issue would completely disappear in a memory safe language and especially one where the wrapper for file descriptors had good prevention of race conditions closing a file, so it hardly seems fair to blame the filesystem API. I mean, in some cases you could blame the API for high frequency of application bugs, but using a closed file seems like a bug category that is unreasonable to do so for. The issue of use-after-free is larger than the filesystem API.
- rfoo 4y agoWriting to arbitrary file descriptor (because, well, a fd is just an integer, right?) is safe Rust. So, no.
- asveikau 4y ago> and especially one where the wrapper for file descriptors had good prevention of race conditions closing a file This is an ownership issue is my point. It's not exactly memory safety but the same concept applied to an integer rather than a pointer.
- chrismorgan 4y agoIf you want to write to an arbitrary file descriptor in Rust, you need to use unsafe code (std::os::unix::io::FromRawFd, unsafe fn from_raw_fd). … though if you’ve got a procfs around, /proc/self/fd/* is ripe for the writing.
- mst 4y agoI have previously used memfd_create plus /proc/self/fd/* to emulate fdlopen on systems that only provide dlopen. I strongly recommend finding a different approach ... but it totally worked.
- moleperson 4y ago
- account42 4y ago> allowing you to delete an open file and providing no means to prevent it I honestly think this one of the greates advantages of POSIX systems over Windows. If I as a (super)user want to delete a file I should always be able to do that and applications will just have to deal with it.
- zvrba 4y agoYou can delete the name/directory entry, but the file remains allocated and in use as long as any program holds it open. So how useful the POSIX behavior is, is debatable.