HN user

zetafunction

256 karma
Posts0
Comments49
View on HN
No posts found.

It's one thing if the library was proactively written with ABI compatibility in mind. It's another thing entirely if the library happens to expose all its implementation details in the headers, making it that much harder to change things.

Disclaimer: I work on Chrome/Blink and I've also contributed a (very small) number of patches to libxml/libxslt.

It's not just a matter of replacing the libxslt; libxslt integrates quite closely with libxml2. There's a fair amount of glue to bolt libxml2/libxslt on to Blink (and WebKit); I can't speak for Gecko.

Even when there's no work on new XML/XSLT features, there's a passive cost to just having that glue code around since it adds quirks and special cases that otherwise wouldn't exist.

Disclaimer: I work on Chrome and I have contributed a (very) small number of fixes to libxml2/libxslt for some of the recent security bugs.

Speaking from personal experience, working on libxslt... not easy for many reasons beyond the complexity of XSLT itself. For instance:

- libxslt is linked against by all sorts of random apps and changes to libxslt (and libxml2) must not break ABI compatibility. This often constrains the shape of possible patches, and makes it that much harder to write systemic fixes.

- libxslt reaches into libxml and reuses fields in creative ways, e.g. libxml2's `xmlDoc` has a `compression` field that is ostensibly for storing the zlib compression level [1], but libxslt has co-opted it for a completely different purpose [2].

- There's a lot of missing institutional knowledge and no clear place to go for answers, e.g. what does a compile-time flag that guards "refactored parts of libxslt" [3] do exactly?

[1] https://gitlab.gnome.org/GNOME/libxml2/-/blob/ca10c7d7b513f3...

[2] https://gitlab.gnome.org/GNOME/libxslt/-/blob/841a1805a9a9aa...

[3] https://gitlab.gnome.org/GNOME/libxslt/-/blob/841a1805a9a9aa...

A large part of the problem is the legacy burden of libxml2 and libxslt. A lot of the implementation details are exposed in headers, and that makes it hard to write improvements/fixes that don't break ABI compatibility.

As someone who had the misfortune of working on clipboard support in Chrome, I thought "wow, there's no way we do that in places other than Linux".

... turns out we do and I helped review that patch. Doh!

For how widely the clipboard is, the actual implementation (both in the OS and in the browser) is surprisingly unloved and unmaintained.

FWIW, Chrome intentionally doesn't plumb through the original image bytes. I wasn't around when it was initially implemented, but even for many years afterwards, there were no (Windows) platform conventions for passing around non-bitmap images on the (Windows) clipboard. And another (probably unintentional) benefit was "the encoded image bytes are from an untrustworthy source and could trigger bugs in buggy image decoders", while bitmaps are (relatively) safe in comparison.

Of course, this is a rather arbitrary line, because it's easy to get the original image bytes out of the sandboxed renderer, e.g. by dragging out the image or by saving the image.

At this point, someone could probably try plumbing through the original bytes or even implementing delayed rendering... but it's quite expensive in terms of time, especially to test all the random things that might break. :(

Oilpan isn't without issues though: finalization causes quite a few headaches, implementation details like concurrent marking make it hard to support things like std::variant, and the interface between Oilpan and non-Oilpan types often introduces opportunities for unsafety.

Templated code can lead to some really long symbol names. As a random tangent, I was trying to figure out why Chrome's stack symbolizer wasn't working for some stack frames this week… and came across this comment in the symbolizer.

  char demangled[256];  // Big enough for sane demangled symbols.
Turns out the longest symbol name in the Chrome binary is 32k characters long... and some of the tests have even longer ones (the longest one I found was 98k characters).

Disclaimer: I am a Chrome developer, who formerly worked on the clipboard.

For a long time, Chrome did not allow pages on the open web to use document.execCommand('copy') or document.execCommand('cut'), and there was a fairly steady stream of requests from web developers to enable this. Eventually, Chrome did expose this gated behind a user gesture: https://chromestatus.com/feature/5223997243392000

So instead of changing their new tab page to require a gesture like all other sites... they decided to allow any website to copy text into the clipboard. Nice.

Ownership of the clipboard features has moved around a bit, and sometimes historical context around things like the user gesture requirement are lost. Here, the NTP doesn't actually need this to work without a user gesture. The correct fix here is to fix the NTP tests to correctly simulate a user gesture, not to allow writing to the clipboard without a user gesture.

I think copying into the clipboard needs an overhaul—even with a gesture. Don't you hate when news sites add a "- from XYZ" to your clipboard? That shouldn't be possible. I'm not sure how you'd fix this, but it should be fixed.

This is a difficult problem to fix. There are absolutely websites that abuse this. But there are also pages that do use the legacy clipboard API events in non-abusive ways (e.g. rich text editors), and blocking this outright would break legitimate uses as well.

Maybe something like a "copy as plain text" option would make sense...

My personal experience from reading code that uses Chromium's C++ garbage collector is that that's often not true. While there might no longer be use-after-free errors, it's also no longer possible to make assertions like "object X must outlive Y" because object Y could be referenced arbitrarily and kept alive longer than expected. To get around that, objects might have an explicit shutdown step. But shutting down a large graph of objects is often fraught with peril, especially when that work can span multiple processes.

Chromium's object graph, for better or worse, has a lot of nodes and edges. Operations like tearing down a document that's navigating away are full of complexity. Executing JS is fraught with peril, since it's possible that objects currently on the C++ stack will get destroyed by an operation triggered by user JS. The modern web is just really complicated.

Non-owning pointers are absolutely a problem if the object graph is large and complex enough. Many objects in Chromium have lifetimes managed by smart pointers, but unfortunately, that doesn't do anything to protect against code that mistakenly violates an invariant like "object X must always outlive Y due to these non-owning pointers". The problem is C++ turns a violation of lifetime invariants into undefined behavior rather than safely crashing.

The object graph in Chromium is extremely complex. Even if an object's lifetime is managed with a smart pointer, there are often raw pointer back references from other objects. And if some of these objects also have lifetimes coupled to IPC or work happening on other threads… well, you can imagine the result :)

Of course, one could make all the back references weak, but that turns out to also significantly hinder understanding of the code. It ends up being really easy to lose all assertions about when any given object that's weakly reference is actually supposed to be live, and you end up with code that's littered with checks everywhere "just in case".

There's actually been quite a bit of work to bounds check accesses for containers implemented inside Chromium, such as span and optional, but it's harder to get these checks into upstream libc++.

GC is one way to reduce the number of memory safety issues, but there are often tricky interactions between GC and non-GC code. Another issue with GC is it's often much harder to reason about object lifetimes.

The Rule of 2 7 years ago

1. The only option for avoiding an unsafe implementation language is currently Java, which is limited to Android.

2. Avoiding high privilege often means going out of process, which is challenging on resource-constrained devices.

Do they distrust their own coders to the same degree they distrust the processes they sandbox? I suspect the answer to this is "no" but I would like to hear from someone who actually codes there.

Actually, the answer is yes. That's why the renderer process is sandboxed.

Let's just collectively admit it, finally - you can't write safe C++ in a codebase this complex.

I work on Chrome. The working assumption is there always exists a bug that allows remote code execution in the renderer.

There are three separate attacks.

Site isolation mitigates variant 1 of Spectre, which allows same-process reads.

It doesn't protect against variant 2 of Spectre, which could allow cross-process reads. While this is believed to be much harder to exploit than the first variant, there are several mitigations in development:

- Reduce the reliability of timing gadgets from JS

- Compiler defenses like LLVM's -mretpoline

- Intel's IBRS microcode update

As you mentioned, site isolation also won't help against Meltdown, which allows disclosure of kernel memory: this requires the kernel page table isolation patches.

Note that it's not just tabs sharing processes that's an issue: prior to the site isolation work, any iframe in the same page would always be in the same process as the main frame. With site isolation, it's possible to host cross-site [1] iframes in a separate process.

[1] Two pages are considered cross-site if they cannot use document.domain to become same origin. In practice, this means that the effective TLD + 1 component match.