[HN Gopher] The undocumented Android change that led to aCropaly...
       ___________________________________________________________________
        
       The undocumented Android change that led to aCropalypse was
       reported during beta
        
       Author : luu
       Score  : 90 points
       Date   : 2023-03-30 20:01 UTC (1 days ago)
        
 (HTM) web link (iliana.fyi)
 (TXT) w3m dump (iliana.fyi)
        
       | TazeTSchnitzel wrote:
       | I wonder if some developer of an app working with ZIP files will
       | have noticed this bug and had to update their app. ZIP is an
       | unusually terrible file format because it has a footer rather
       | than a header, so the truncation bug would lead to a ZIP file
       | with two headers!
        
         | canucker2016 wrote:
         | Some ZIP-file handling apps go straight to the end of the ZIP
         | file and work backwards looking for the central directory.
         | 
         | I believe others will start from the beginning of the file and
         | parse each local directory header and 1) build up their own
         | central directory truth, or 2) stop once they've reached the
         | start of the central directory.
         | 
         | I recall that the footer-only format was to enable streaming
         | out a ZIP file esp. when rewinding to the front of the
         | file/stream was not possible, like sending over a network - at
         | least that's one of the use cases I recall.
        
       | amethyst wrote:
       | Unrelated: the nostalagia of iliana's favicon is incredible. So
       | much time spent on old modems watching that animation spin while
       | waiting for pages to slowly load in...
        
         | neilv wrote:
         | That one was great, and stylish.
         | 
         | The original NCSA Mosaic one with the globe could've been a bit
         | much, but it was appropriate, given the impact of the Web, even
         | for pre-Web Internet natives: "OMG, this is a tingly step
         | forward in distributed global hypertext, it's happening right
         | now -- interacting around the world, and also superpowering the
         | world." With implied altruistic and benevolent goodness.
         | 
         | (The earlier Mcom/Netscape throbbing "N", when I guess they
         | were still rebranding Navigator from Mosaic, looked silly even
         | at the time. Then they did a good one. And there were a bunch
         | of creative alternatives, including some easter egg ones, which
         | JWZ cataloged at one time.)
        
       | neilv wrote:
       | Kudos to the engineer who originally not only identified the
       | problem, but also raised the concern about breaking behavior, and
       | about documenting it.
       | 
       | Looks like the ball was first dropped when the issue was deferred
       | without acknowledging the reporter's concise suggestions of how
       | to handle it, engineering-wise. Nor a sign that existing code was
       | reviewed for the same problem.
       | 
       | Then looks like the ball was dropped again, in what I'm guessing
       | might've been: "old version, forget all the old open issues, all
       | the issues that still apply, someone will rediscover the hard
       | way".
       | 
       | This "aCropalypse" event is an example of how making the wrong
       | triage of an issue report can turn out very expensive. All the
       | costs to the world of aCropalypse could've been averted. Some of
       | those costs might eventually come back to the company.
       | 
       | It's easy to guess why the problem happened and the report wasn't
       | handled responsibly. By the time the problem exhibited in a way
       | that couldn't be ignored, the pertinent metrics/KPIs/OKRs about
       | clearing issues, resource allocations, and product shipment
       | schedules were already in the past, bonuses had been paid,
       | promotions for shipping new things earned, etc. And security
       | problems are treated as inevitable, even if there's a constant
       | stream of them, and they're produced faster than they're fixed.
       | 
       | We're going to need software engineers to be accountable for
       | things we've done and signed off on.
       | 
       | (Bonus if accountability changes happen near-term: GPT-powered
       | open source laundering gets a pause, because bridges start
       | falling on everyone foolish enough to sign off on that mangled
       | statistical plagiarism.)
        
       | justin_oaks wrote:
       | Another case of poorly chosen defaults leading to bugs, leading
       | to security issues.
       | 
       | Also a case of violating the Principle of Least Astonishment [0].
       | How many people would expect that when you write a file you would
       | also have specify that you want it truncated?
       | 
       | [0]
       | https://en.wikipedia.org/wiki/Principle_of_least_astonishmen...
        
         | [deleted]
        
         | intelVISA wrote:
         | Some say certain agencies wanted it this way.
        
         | jandrese wrote:
         | It's notable that in practically every other programming
         | language on every platform the default is to truncate the file
         | to 0 bytes when opened in write (not append) mode. This isn't
         | one of those things where there are two schools of thought
         | where people take sides. It's a completely baffling decision to
         | buck all convention vs. common sense.
        
           | Someone wrote:
           | > It's notable that in practically every other programming
           | language on every platform the default is to truncate the
           | file to 0 bytes when opened in write (not append) mode
           | 
           | Examples of 'Open' calls that require setting a bit in the
           | 'flags' argument to truncate an existing file:
           | 
           | - https://learn.microsoft.com/en-
           | us/windows/win32/api/winbase/...
           | 
           | -
           | https://man.freebsd.org/cgi/man.cgi?query=open&sektion=2&n=1
        
       | canucker2016 wrote:
       | Mentioned in the link.
       | 
       | This seems to be the original PR for the API in question,
       | ParcelFileDescriptor#parseMode():
       | 
       | https://android.googlesource.com/platform/frameworks/base/+/...
       | +    public static int parseMode(String mode) {         +
       | final int modeBits;         +        if ("r".equals(mode)) {
       | +            modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
       | +        } else if ("w".equals(mode) || "wt".equals(mode)) {
       | +            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
       | +                    | ParcelFileDescriptor.MODE_CREATE         +
       | | ParcelFileDescriptor.MODE_TRUNCATE;         +        } else if
       | ("wa".equals(mode)) {         ...
       | 
       | You can see that "w" and "wt" map to the same mode for file
       | operations, including the MODE_TRUNCATE flag.
       | 
       | But we jump ahead a few years and the code is reworked.
       | 
       | from
       | https://android.googlesource.com/platform/frameworks/base/+/...:
       | public static int parseMode(String mode) {         -        final
       | int modeBits;         -        if ("r".equals(mode)) {         -
       | modeBits = ParcelFileDescriptor.MODE_READ_ONLY;         -
       | } else if ("w".equals(mode) || "wt".equals(mode)) {         -
       | modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY         -
       | | ParcelFileDescriptor.MODE_CREATE         -                    |
       | ParcelFileDescriptor.MODE_TRUNCATE;         -        } else if
       | ("wa".equals(mode)) {         ...         -        return
       | modeBits;         +        return FileUtils.translateModePosixToP
       | fd(FileUtils.translateModeStringToPosix(mode));              }
       | 
       | So what does FileUtils.translateModePosixToPfd(FileUtils.translat
       | eModeStringToPosix(mode)) do?
       | 
       | from
       | https://android.googlesource.com/platform/frameworks/base/+/...:
       | +    public static int translateModeStringToPosix(String mode) {
       | +        int res = 0;         +        if (mode.startsWith("rw"))
       | {         +            res |= O_RDWR | O_CREAT;         +
       | } else if (mode.startsWith("w")) {         +            res |=
       | O_WRONLY | O_CREAT;         +        } else if
       | (mode.startsWith("r")) {         +            res |= O_RDONLY;
       | +        } else {         +            throw new
       | IllegalArgumentException("Bad mode: " + mode);         +        }
       | +        if (mode.indexOf('t') != -1) {         +            res
       | |= O_TRUNC;         +        }         +        if
       | (mode.indexOf('a') != -1) {         +            res |= O_APPEND;
       | +        }         +        return res;         +    }
       | 
       | Now "w" and "wt" map to different Posix open mode flags since one
       | has to explicitly pass in "t" to get file truncation.
       | 
       | A dev was careless, plain and simple.
       | 
       | Having code in a monorepo doesn't help if you don't check how
       | your code changes affects consumers/clients of the code.
       | 
       | If they couldn't change translateModeStringToPosix() behaviour,
       | then the dev could've amended parseMode() to map "w' to "wt" to
       | keep the old behaviour (ugly and non-orthogonal, yes, but that's
       | the way the API was designed).
        
         | canucker2016 wrote:
         | Looking at the reworked parseMode API some more and it looks
         | like a total mess.
         | 
         | Original code would only accept one of these modes: "r", "w",
         | "wt", "wa", "rw", "rwt".
         | 
         | Anything else, and you'll get an IllegalArgumentException
         | exception.
         | 
         | which matches the docs:
         | 
         | from
         | https://developer.android.com/reference/android/os/ParcelFil...
         | Parameters         mode String: The string representation of
         | the file mode. Can be "r", "w", "wt", "wa", "rw" or "rwt".
         | Throws         IllegalArgumentException if the given string
         | does not match a known file mode.
         | 
         | ----
         | 
         | The modified parseMode code will only throw the
         | IllegalArgumentException if the mode string does NOT start with
         | "rw", "w", or "r".
         | 
         | You can append any other character(s) to the mode string ("a",
         | "t", "x", "y", "z", your fav emoji char), and you won't get an
         | IllegalArgumentException exception now.
         | 
         | I wonder what happens when open() gets a mode flag with both
         | "a" and "t" set?
         | 
         | parseMode API contract thrown out the window...
        
       | btown wrote:
       | A cautionary tale here: when you are testing, if you mock out
       | even low-level I/O utilities, you're vulnerable to those
       | utilities changing specification subtly. You can supply-chain
       | attack yourself by simply updating a library.
       | 
       | Full filesystem/service-engaging integration tests are helpful
       | but far from exhaustive - here, one wouldn't have just needed to
       | read the cropped file using standard display systems and count
       | the pixels, but hash the underlying file in its entirety.
       | 
       | For critical libraries in an I/O pipeline, encourage team members
       | to read CHANGELOGs - both before choosing a library to make sure
       | they're maintained, and closely when even doing a minor upgrade.
        
       ___________________________________________________________________
       (page generated 2023-03-31 23:00 UTC)