Implementing fully immutable files
an extraordinary way to destroy everything".
The chattr man page is clear on what happens when the immutable bit is set:
This description is true for the most part (at least on filesystems supporting this bit), but Wong noticed an important exception: a process that opens a file for writing prior to the setting of the immutable bit will still be able to write to the file after the bit is set for as long as it holds the file descriptor open. So while many operations on an immutable file are blocked, modifying the data in the file using open file descriptors is still allowed. The file is not yet, in other words, fully immutable.
That behavior is both inconsistent and surprising; Wong set out to change it by making the system actually behave the way the man page says it will. Doing that requires two types of changes, the first of which is easier than the second. Whenever a process attempts to write to a file descriptor, a call is made to generic_write_checks() to ensure that the operation can be allowed. Adding a check for immutability to that function will cause write() calls to fail immediately once a file has been marked immutable. A similar check needs to be added to do_mmap() to prevent the creation of a writable memory mapping from a file descriptor. Those changes close off the most obvious ways to change an immutable file.
The remaining problem is that writable memory mappings of the file may already exist, and those, too, can be used to modify a file that has since been marked immutable. User-space code need not make any system calls to write to a memory-mapped region, so there isn't a single, simple place to add a check like there is with write() and mmap(). The good news is that most of the needed machinery to prevent such writes is already in place, thanks to how filesystems manage writable mappings now.
The problem that a filesystem implementation has to solve is that it, too, will get no notification when a process writes to a region of memory that has a file mapped into it; user space simply dereferences a pointer and stores data there. But the filesystem needs to know when that happens so it can ensure that the newly written data eventually finds its way to persistent storage. The trick that is used here is to write-protect the pages in memory. When user space attempts to write to one of those pages, a page fault will result; the kernel can then make the page writable and notify the filesystem that the page has been written to. When the filesystem code eventually writes the modified page(s) back to disk, it can once again write-protect those pages to mark them as being clean and to catch any subsequent modifications.
One obvious place, then, to prevent modification of an immutable file is when that page fault occurs; rather than allow the modification to proceed, the kernel can fail the operation and deliver a SIGBUS signal to user space. But even that doesn't catch the case where pages have already been marked as being writable; user space could continue to make changes to those.
Closing that last hole requires making changes to every filesystem that is to support the new behavior; that is the object of the bulk of the patches in Wong's set. Filesystem implementations already have an ioctl() handler that will be called when the immutable bit is set, so that is the logical place to respond when the status of a file is changed. A number of things need to happen at that point. If there are direct I/O operations outstanding, they must be allowed to conclude before marking the file immutable. Then, any pages that are currently dirty need to be flushed out to permanent storage; those changes were made prior to the file being marked immutable, so they should persist. Finally, all pages mapped from the file in question can be marked read-only, at which point they cannot be modified further. In most filesystems, flushing dirty pages and write-protecting them is already implemented as a single operation, so it's just a matter of calling the right function.
The end result is that, once a file is marked immutable, it truly cannot be
changed further — at least, until a privileged user clears the immutable
bit. This is, of course, a change in the kernel's behavior; any
application that relies on the ability to write to an open file descriptor
for an immutable file will break. Hyrum's law says that this is certain
to happen somewhere; that would likely lead to the reversion of this patch
set. In practice, though, it seems entirely possible that nobody actually
depends on this obscure behavior, so Wong's patch set will fail to destroy
everything as advertised.
Index entries for this article | |
---|---|
Kernel | Filesystems |
Posted Apr 19, 2019 15:46 UTC (Fri)
by nivedita76 (subscriber, #121790)
[Link]
Some other attributes are also noted to not be implemented yet.
Also, even for chmod(2) isn't it filesystem-dependent what happens to already open file descriptors if a file is made read-only for eg? The manpage notes that NFS may apply that permission immediately to open files, and presumably local filesystems wouldn't.
Posted Apr 19, 2019 16:58 UTC (Fri)
by NYKevin (subscriber, #129325)
[Link] (4 responses)
Posted Apr 19, 2019 17:36 UTC (Fri)
by derobert (subscriber, #89569)
[Link] (2 responses)
Posted Apr 19, 2019 22:25 UTC (Fri)
by NYKevin (subscriber, #129325)
[Link] (1 responses)
Posted Apr 22, 2019 12:55 UTC (Mon)
by bandrami (guest, #94229)
[Link]
Honestly I despise them from a security standpoint, because of the change in reasoning required you demonstrated. More complex is bad, and non-obviously more complex is even worse.
Posted Apr 20, 2019 20:43 UTC (Sat)
by luto (guest, #39314)
[Link]
Posted Apr 19, 2019 17:08 UTC (Fri)
by augustz (guest, #37348)
[Link] (5 responses)
Posted Apr 19, 2019 17:47 UTC (Fri)
by epa (subscriber, #39769)
[Link] (4 responses)
Posted Apr 19, 2019 19:03 UTC (Fri)
by augustz (guest, #37348)
[Link]
Posted Apr 19, 2019 19:47 UTC (Fri)
by mb (subscriber, #50428)
[Link]
Posted Apr 21, 2019 11:19 UTC (Sun)
by tao (subscriber, #17563)
[Link] (1 responses)
Posted Apr 21, 2019 18:11 UTC (Sun)
by Cyberax (✭ supporter ✭, #52523)
[Link]
Posted Apr 19, 2019 21:47 UTC (Fri)
by wahern (subscriber, #37304)
[Link] (9 responses)
It's also inconsistent. You can continue writing to a file through an existing file descriptor after removing write permissions from the file. Why should immutability be any different? And in my limited experience using extended attributes like immutability, the principal use case seems to be making backup and restore procedures more fail-safe. For the security case, that an existing process already had write permissions suggests the ability to revoke after the fact is a niche feature that doesn't contribute much from a systems engineering standpoint.
Posted Apr 20, 2019 2:44 UTC (Sat)
by epa (subscriber, #39769)
[Link] (8 responses)
(You can compare the different meanings of ‘const’ or ‘readonly’ in languages like C++, Java and C# for another example of how an object can be read-only for you, or have a positive guarantee that it won’t be mutated while you hold a reference to it. Locking in database systems is another place where you have to separate the two meanings.)
You may be right that in practice it makes little difference. Immutable files are a specialized feature. But if you’re going to have them at all, surely they should be implemented properly. A guarantee of immutability isn’t worth much unless it holds all of the time.
Posted Apr 20, 2019 5:59 UTC (Sat)
by lkundrak (subscriber, #43452)
[Link] (7 responses)
Unless you, of course, turn off the immutable bit.
I'm quite honestly having trouble finding it hard to understand the use case where the immutable files provide any sort of useful guarantee.
Posted Apr 20, 2019 12:21 UTC (Sat)
by mikemol (guest, #83507)
[Link]
I could easily construct a high-confidence system that relied on the audit framework to tell me when someone is playing with the immutable bit without the overhead of logging on every write attempt to the file in question, for example. Then, I can rely on the immutable bit (with some restrictions) as I make an assertion that a file does not, shall not change.
I can see use cases in antivirus frameworks, configuration management frameworks, logging and auditing frameworks, and so on. Effectively, any system where demonstrable, positive control over a system accessible to untrusted individuals.
It's not a magic bullet, but a useful armor layer.
Posted Apr 20, 2019 13:01 UTC (Sat)
by epa (subscriber, #39769)
[Link] (3 responses)
Posted Apr 22, 2019 16:39 UTC (Mon)
by wahern (subscriber, #37304)
[Link] (2 responses)
I'm sure some people would find the proposed behavior of revoking mmap access useful, too. But the additional complexity is *tremendous* and, IMO, not worth the marginal benefit, even if there are a handful of organizations that *must* have the feature. I mean, if they really need such behavior they can always just terminate all processes with open file handles after making a file immutable. Messy, but at least the mess doesn't become a long-term maintenance burden for everybody else. It's a dubious guarantee, anyhow, considering how easy it will be to accidentally break the invariant.
Posted Apr 22, 2019 19:44 UTC (Mon)
by wahern (subscriber, #37304)
[Link]
Posted Apr 22, 2019 20:15 UTC (Mon)
by rweikusat2 (subscriber, #117920)
[Link]
"no data can be written to the file"
someone apparently noted that this wasn't accurate and corrected to documentation to
"the file can not be opened in write mode."
I seriously doubt that there's any organisation on this planet which suddenly "must" have this feature. Methinks this is more something like a bored Oracle guy making undirected changes to a codebase (possibly "a sufficiently well-connected, bored Oracle guy that such undirected changes actually get accepted instead of being stonewalled").
Posted Apr 21, 2019 0:30 UTC (Sun)
by mm7323 (subscriber, #87386)
[Link]
As for security guarantees, wouldn't something like SElinux be more appropriate, fine grained and auditable than this mechanism? That said, I have no idea if SElinux or similar behave sanely if policy is changed while files are already opened or memory mapped...
Posted Apr 22, 2019 15:15 UTC (Mon)
by janfrode (subscriber, #244)
[Link]
https://www.ibm.com/developerworks/community/wikis/home?l...
Posted Apr 19, 2019 22:08 UTC (Fri)
by Freeaqingme (subscriber, #103259)
[Link] (1 responses)
Posted Apr 20, 2019 1:34 UTC (Sat)
by Paf (subscriber, #91811)
[Link]
Posted Apr 20, 2019 10:53 UTC (Sat)
by TheGopher (subscriber, #59256)
[Link]
Posted Apr 20, 2019 15:44 UTC (Sat)
by rweikusat2 (subscriber, #117920)
[Link] (9 responses)
Posted Apr 20, 2019 16:16 UTC (Sat)
by gus3 (guest, #61103)
[Link] (5 responses)
2. Root checks for open descriptors on A, terminates User's process.
3. The parent process of P notes abnormal termination, re-launches P, which re-opens A.
4. Root sets immutable bit on A, but... too late, the file isn't immutable for User.
Or, just take the system down to single-user, to make sure *nobody* has access except Root; set the immutable bit(s) in question; then return to multi-user.
The current implementation of immutable (and append-only) files forces a sysadmin to choose between guaranteed behavior and uptime, a choice that the sysadmin's boss might not (will not) understand.
Posted Apr 20, 2019 22:09 UTC (Sat)
by epa (subscriber, #39769)
[Link]
Posted Apr 21, 2019 21:01 UTC (Sun)
by rweikusat2 (subscriber, #117920)
[Link]
Posted Apr 23, 2019 16:42 UTC (Tue)
by sorokin (guest, #88478)
[Link] (2 responses)
I would say that the problem is the order of the operations. Root should set the immutable bit first and then terminate processes that use the file.
Posted Apr 24, 2019 16:46 UTC (Wed)
by epa (subscriber, #39769)
[Link] (1 responses)
Posted Apr 25, 2019 16:10 UTC (Thu)
by rweikusat2 (subscriber, #117920)
[Link]
The change described in the article doesn't "fix" anything, it just replaces the documented semantics with something else.
Posted Apr 21, 2019 9:06 UTC (Sun)
by matthias (subscriber, #94967)
[Link] (2 responses)
I would rather think that immutable and append only are not file permissions and therefore it might be sensible to treat them differently.
Posted Apr 22, 2019 11:44 UTC (Mon)
by rweikusat2 (subscriber, #117920)
[Link] (1 responses)
'Immutable' is a bit messy because it both affects accesses to the file data and changes to the i-node. But "this file cannot be opened for writing" is a perfectly well-defined effect. It's also different from the ordinary write permissions because files marked as immutable also cannot be opened for writing by privileged processes while the bit is set. *If* some kind of retroactively effective access permission change is actually needed for something, ie, this is not just about an impedance mismatch between someone's idea of the English meaning of immutable and the technical term 'immutable file', it would be better to implement this as a different property than to change the semantics of an existing one.
There's also no documented error code which could be returned by write to indicate "your access has been revoked", hence, existing code supposed to handle transient file system misconfiguration automatically is pretty much guaranteed to be broken by this.
Posted Apr 22, 2019 15:04 UTC (Mon)
by epa (subscriber, #39769)
[Link]
Posted Apr 21, 2019 19:24 UTC (Sun)
by ncm (guest, #165)
[Link] (6 responses)
The fundamental problem here is that between creating, writing, and marking the file, another process can slip in and alter the file, so that after marking it, you still have to go back and check that it was not corrupted before it was marked. This last step is likely to be omitted in real code.
Alternatively, you make an unreadable directory and a file with an unguessable name, write the file, mark it, and then mv it into place. But does mv even work on a file so marked, if I can't link() it?
Posted Apr 21, 2019 19:33 UTC (Sun)
by ncm (guest, #165)
[Link]
Posted Apr 21, 2019 22:35 UTC (Sun)
by luto (guest, #39314)
[Link]
Posted Apr 22, 2019 14:44 UTC (Mon)
by rweikusat2 (subscriber, #117920)
[Link] (3 responses)
Posted Apr 23, 2019 16:41 UTC (Tue)
by matthias (subscriber, #94967)
[Link] (2 responses)
Posted Apr 23, 2019 17:01 UTC (Tue)
by rweikusat2 (subscriber, #117920)
[Link]
BTW, concluding that the racyness is not a necessary consequence of the shared filesystem namespace by coming up with a contrived example built on avoiding use of the shared filesystem namespace is 'interesting' reasoning. Why is it supposed to be avoided if it's not the cause of the problem?
Posted Apr 25, 2019 8:06 UTC (Thu)
by thithib (guest, #115203)
[Link]
Posted Apr 22, 2019 16:28 UTC (Mon)
by meuh (guest, #22042)
[Link] (1 responses)
Posted Apr 22, 2019 17:48 UTC (Mon)
by rweikusat2 (subscriber, #117920)
[Link]
Posted Apr 23, 2019 17:53 UTC (Tue)
by xorbe (guest, #3165)
[Link] (1 responses)
Posted Apr 24, 2019 19:04 UTC (Wed)
by nix (subscriber, #2304)
[Link]
Implementing fully immutable files
"Setting 'a' and 'i' attributes will not affect the ability to write to already existing file descriptors."
Implementing fully immutable files
In practice, though, it seems entirely possible that nobody actually depends on this obscure behavior, so Wong's patch set will fail to destroy everything as advertised.
Postulate that the process writing through mmap() knows nothing of the immutable bit, and does not own the file in question. Then the patch is taking an entirely legal pointer dereference and turning it into a segfault. A segfault caused not only by a different process, but by a different user (who cannot call kill(pid, SIGBUS) directly). Maybe that's improbable, but I find it rather frightening all the same.
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Please avoid this on Linux wherever possible.
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
(Debian 7 man page)
(http://man7.org/linux/man-pages/man1/chattr.1.html)
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
DAX
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
> 4. Root sets immutable bit on A, but... too late, the file isn't immutable for User.
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
Implementing fully immutable files
After reading the thread (1, 2, 3, 4) about And what about DMA
get_user_pages()
and corruptions/crashes happening when a mmap()
ed file is writing to through DMA, I wonder what will happen when a peripheral will try to DMA-write to a memory mapped file toggled immutable ...
And what about DMA
Implementing fully immutable files
Implementing fully immutable files