[HN Gopher] How to Secure Existing C and C++ Software Without Me...
___________________________________________________________________
How to Secure Existing C and C++ Software Without Memory Safety
[pdf]
Author : aw1621107
Score : 121 points
Date : 2025-03-31 07:36 UTC (15 hours ago)
(HTM) web link (arxiv.org)
(TXT) w3m dump (arxiv.org)
| m00dy wrote:
| Looks like they're finally coming to terms with C++'s flaws.
| michaelsshaw wrote:
| "they"
| pjc50 wrote:
| Short paper, so can be easily summarized. The claim is that
| security can be improved by these compiler and hardware assisted
| measures: - Stack Integrity - Control-
| Flow Integrity - Heap Data Integrity - Pointer
| Integrity and Unforgeability
|
| They cite the deployment of these measures on recent Apple
| hardware as evidence of their effectiveness.
| pastage wrote:
| Exciting! I did not know about HW pointer authentication and it
| is ten years old now on ARM, the referenced papers are nice but
| honestly LLVM docs are a lot better.
|
| https://clang.llvm.org/docs/PointerAuthentication.html
|
| The papers:
| https://www.usenix.org/conference/usenixsecurity23/presentat...
| https://www.qualcomm.com/content/dam/qcomm-martech/dm-assets...
| MattPalmer1086 wrote:
| This looks really useful. Doesn't fix the problem of memory
| corruption but mostly seems to limit the ability to convert that
| into remote code execution. And all the techniques are already in
| widespread use, just not the default or used together.
|
| I would not be surprised if attackers still manage to find sneaky
| ways to bypass all the 4 protections, but it would certainly
| raise the bar significantly.
| lambdaone wrote:
| It's already been demonstrated to be insufficient; otherwise
| Apple software would now be impregnable. That's not to say
| these protections are a bad idea; they should be universal as
| they substantially reduce the existing attack surface - but the
| paper massively over-sells them as a panacea.
| pron wrote:
| Most server software these days is already written in memory-
| safe languages yet still has security vulnerabilities. If you
| reduce the vulnerabilities due to memory safety issues by,
| say, 90%, then there's nothing special about them anymore. On
| the other hand, memory-safe languages also don't fully
| eliminate memory safety issues as they depend on operations
| or components that may not themselves be fully memory-safe
| (BTW, Rust has this problem more than other memory-safe
| languages).
|
| So a "panacea" in this context doesn't mean making the stack
| memory-safe (which nobody does, anyway) but rather making the
| vulnerabilities due to memory safety no more common or
| dangerous than other causes of vulnerabilities (and remember
| that rewriting software in a new language also introduces new
| vulnerability risks even when it reduces others). Whether
| these techniques actually accomplish that or not is another
| matter (the paper makes empirical claims without much
| evidence, just as some Rust fans do).
| TheDong wrote:
| > BTW, Rust has this problem more than other memory-safe
| languages
|
| Do you have a citation there?
|
| I've run into a ton of memory safety issues in Go because
| two goroutines concurrently modifying a map or pointer is a
| data-race, which leads to memory unsafety... and go makes
| it wildly easy to write such data-races. You need to
| manually add mutexes everywhere for go to be memory safe.
|
| Rust, on the other hand, I've yet to run into an actual
| memory safety issue. Like, I'm at several hundred in Go,
| and 0 in rust.
|
| I'm curious why my experience is so different.
| commandersaki wrote:
| I think the key is _" as they depend on operations or
| components that may not themselves be fully memory-safe"_
| indicating Rust is used a far bit more to do interop with
| memory unsafe languages unlike Go or Java.
| meltyness wrote:
| I sense OP's referring to raw pointer operations
| permitted by the unsafe dialect of the language.
| pron wrote:
| It is more common (and/or more necessary) to either write
| unsafe code or call out to unsafe code written in some
| other language in Rust than in Java.
|
| BTW, I wouldn't call a language that violates memory
| safety without an explicit unsafe operation a memory-safe
| language.
| fpoling wrote:
| Rust compiler assumes that unsafe code still follows the
| borrow rules and optimizes code based on that. So if that
| is not the case, the consequences can be bad. For
| example, there was a bug in Rust OpenSSL bindings where
| OpenSSL had not documented the proper life time of some
| of the function arguments/return types. As the result
| unsafe code containing the call to OpenSSL C
| implementation violated Rust memory model leading to
| crashes.
|
| But I agree that such problems are rare in Rust and Go
| races are much more problematic.
| sebstefan wrote:
| >A Pragmatic Security Goal
|
| >Remote Code Execution (RCE) attacks where attackers exploit
| memory-corruption bugs to achieve complete control are a very
| important class of potentially-devastating attacks. Such attacks
| can be hugely disruptive, even simply in the effects and economic
| cost of their remediation [26]. Furthermore, the risk of such
| attacks is of special, critical concern for server-side platform
| foundations [10]. Greatly reducing the risk of RCE attacks in C
| and C++ software, despite the presence of memory-corruption bugs,
| would be a valuable milestone in software security especially if
| such attacks could be almost completely prevented. We can,
| therefore, aim for the ambitious, pragmatic goal of preventing
| most, or nearly all, possibilities of RCE attacks in existing C
| and C++ software without memory safety. Given the urgency of the
| situation, we should only consider existing, practical security
| mechanisms that can be rapidly deployed at scale.
|
| I don't know if it's obvious to anyone else that this is AI-
| written or if it's just me/if I'm mistaken
| dgellow wrote:
| It's not obvious to me. I cannot say one way or the other
| readingnews wrote:
| I am not sure, and it may be this persons culture/background,
| but I do know that at a college/uni, your advisors/reviewers
| would tell you not to do the adjective/drama stuff, as it adds
| no real value to a scientific/technical paper.
|
| e.g. potentially-devastating, hugely disruptive, special
| critical, greatly reducing, valuable milestone, almost
| completely, ambitious pragmatic, most or nearly all, existing
| practical.
| cadamsdotcom wrote:
| > For high assurance, these foundations must be rewritten in
| memory- safe languages like Go and Rust [10]; however, history
| and estimates suggest this will take a decade or more [31].
|
| The world runs on legacy code. CISA is correct that rewrites are
| needed for critical software [1][2] but we know how rewrites tend
| to go, and ROI on a rewrite is zero for most software, so it will
| take far more than a decade if it happens at all. So score one
| for pragmatism with this paper! Hope CISA folks see it and update
| their guidance.
|
| [1] https://www.cisa.gov/news-events/news/urgent-need-memory-
| saf... [2] https://www.cisa.gov/resources-tools/resources/case-
| memory-s...
| berratype wrote:
| I don't know if the ROI on rewrites is zero, following
| Prossimo's work, I'm seeing lots of performance and
| maintainability improvements over existing software.
| DyslexicAtheist wrote:
| > Hope CISA folks see it and update their guidance.
|
| cope > hope @ CISA right now:
|
| https://www.darkreading.com/cyberattacks-data-breaches/cisa-...
| 0xEF wrote:
| I wish HN readers were not so afraid to openly discuss this
| more, but it is a much bigger issue than even what that
| article lets on (which is a good article, I might add). Cuts
| to critical agencies like this aren't about efficiency at
| all; it's about ham-stringing roadblocks that might be in
| your way. That means we can expect one of two things in the
| near future of the US;
|
| 1. Enemies of the US gov't exploit the weakness, possibly
| conspiring with the people who created the weakness to do so.
|
| 2. A near or complete collapse of the government as, lo and
| behold, it is discovered that none of them actually knew what
| they were doing, regardless of the confidence in which they
| said otherwise.
|
| Either way, we, the people trying to keep industries moving
| and bring home food to put on the table, will suffer.
| Xylakant wrote:
| I don't see what updates you expect on CISA guidance because
| the very documents you reference already acknowledge that
| rewriting all code is not a viable strategy in the general case
| and that other options for improvement exist
|
| For example, they recommend evaluating hardware backed
| solutions such as CHERI
|
| > There are, however, a few areas that every software company
| should investigate. First, there are some promising memory
| safety mitigations in hardware. The Capability Hardware
| Enhanced RISC Instructions (CHERI ) research project uses
| modified processors to give memory unsafe languages like C and
| C++ protection against many widely exploited vulnerabilities.
| Another hardware assisted technology comes in the form of
| memory tagging extensions (MTE) that are available in some
| systems. While some of these hardware-based mitigations are
| still making the journey from research to shipping products,
| many observers believe they will become important parts of an
| overall strategy to eliminate memory safety vulnerabilities.
|
| And acknowledge that strategies will need to be adjusted for
| every case
|
| > Different products will require different investment
| strategies to mitigate memory unsafe code. The balance between
| C/C++ mitigations, hardware mitigations, and memory safe
| programming languages may even differ between products from the
| same company. No one approach will solve all problems for all
| products.
|
| However, and that's where the meat of the papers is: They
| require you to acknowledge that there is a problem and do
| _something_ about it
|
| > The one thing software manufacturers cannot do, however, is
| ignore the problem. The software industry must not kick the can
| down the road another decade through inaction.
|
| and the least you can do is _make it a priority_ and _make a
| plan_
|
| > CISA urges software manufacturers to make it a top-level
| company goal to reduce and eventually eliminate memory safety
| vulnerabilities from their product lines. To demonstrate such a
| commitment, companies can publish a "memory safety roadmap"
| that includes information about how they are modifying their
| software development lifecycle (SDLC) to accomplish this goal.
|
| It's clearly not the case that these papers say "Rewrite all in
| Rust, now!". They do strongly advocate in favor of using memory
| safe languages for future development and I believe that's the
| rational stance to take, but they appear well groundet in their
| stance on existing software.
| IshKebab wrote:
| Google has shown that you get the biggest benefit by writing
| _new_ code in memory-safe languages, so it 's not like security
| doesn't drastically improve until you've rewritten
| _everything_.
| lambdaone wrote:
| From the cited paper:
|
| "These four types of integrity, do not establish memory safety,
| but merely attempt to contain the effects of its absence;
| therefore, attackers will still be able to change software
| behavior by corrupting memory."
|
| and the paper then goes on to say, about Apple's implementation
| of the cited techniques:
|
| "This intuition is borne out by experience: in part as a result
| of Apple's deployment of these defenses since 2019, the incidence
| of RCE attacks on Apple client software has decreased
| significantly--despite strong attack pressure--and the market
| value of such attacks risen sharply."
|
| "Decreased significantly" is not "eliminated"; indeed, you could
| paraphrase this as "the combination of these techniques has
| already been shown to be insufficient for security guarantees".
|
| Which is not to say that these mitigations are a bad idea; but I
| think their benefits are significantly over-sold in the paper.
| commandersaki wrote:
| I said this in another comment, but an easy way to measure the
| efficacy is to look at the economy surrounding the zero day
| markets. Long story short, over the years it has become
| increasingly more expensive to both produce/supply and acquire
| exploits. This is attributable to the increasing mitigations
| and countermeasures, which include the ones in this document.
| MattPalmer1086 wrote:
| I don't think it is significantly oversold, although it is
| definitely overselling a bit:
|
| "More disconcertingly, in some cases, attacks may be still be
| possible despite the above protections."
|
| "We should prepare for a case where these defenses will fail to
| protect against a specific vulnerability in some specific
| software".
|
| My main concern with the paper is there is no careful analysis
| showing that the 4 techniques they propose are really
| sufficient to cover the majority of RCE exploits. Having said
| that, I don't dispute that having them would raise the bar for
| them a lot.
| mre wrote:
| > However, their use is the exception, not the rule, and their
| use--in particular in combination--requires security expertise
| and investment that is not common. For them to provide real-
| world, large-scale improvements in the security outcomes of using
| C and C++ software, there remains significant work to be done. In
| particular, to provide security benefits at scale, for most
| software, these protections must be made an integral, easy-to-use
| part of the world-wide software development lifecycle. This is a
| big change and will require a team effort.
|
| That's the core problem.
|
| The mechanisms mentioned are primarily attack detection and
| mitigation techniques rather than prevention mechanisms. Bugs
| can't be exploited as easily, but they still exist in the
| codebase. We're essentially continuing to ship faulty software
| while hoping that tooling will protect us from the worst
| consequences.
|
| Couldn't one argue that containers and virtual machines also
| protect us from exploiting some of these memory safety bugs? They
| provide isolation boundaries that limit the impact of exploits,
| yet we still consider them insufficient alone.
|
| It's definitely a step in the right direction, though.
|
| The paper mentions Rust, so I wanted to highlight a few reasons
| why we still need it for people who might mistakenly think this
| approach makes Rust unnecessary: - Rust's
| ownership system prevents memory safety issues at compile time
| rather than trying to mitigate their effects at runtime -
| Rust completely eliminates null pointer dereferencing -
| Rust prevents data races in concurrent code, which the paper's
| approach doesn't address at all - Automatic bounds
| checking for all array and collection accesses prevent buffer
| overflows by design - Lifetimes ensure pointers are never
| dangling, unlike the paper's approach which merely tries to make
| dangling pointers harder to exploit
|
| So, we still need Rust, and we should continue migrating more
| code to it (and similar languages that might emerge in the
| future). The big idea is to shift bug detection to the left: from
| production to development.
| tgv wrote:
| Or Java, or Scala, or Go, or whathaveyou. This is about
| _existing_ software.
| unscaled wrote:
| While all of the languages you mention are memory safe (as is
| almost every programming language released after 1990), none
| of them solve all of the safety problems mentioned above, in
| particular, the two points: - Rust completely
| eliminates null pointer dereferencing - Rust prevents
| data races in concurrent code, which the paper's approach
| doesn't address at all
|
| Scala comes closest to solving these points, since it has
| optional features (in Scala 3) to enable null safety or you
| could build some castles in the sky (with Scala 2) to avoid
| using null and make NPEs more unlikely. The same goes for
| concurrency bugs: you can use alternative concurrency models
| that make data races harder (e.g. encapsulate all your state
| inside Akka actors).
|
| With Go and Java there is no dice. These languages lack the
| expressive power (by design! since they are touted as
| "simple" languages) to do anything that will greatly reduce
| these types of bugs, without resorting external tools (e.g.
| Java static analyzers + annotation or race condition
| checkers).
|
| In short, Rust is one of the only mainstream languages that
| absolutely guarantees safety from race conditions, and
| complete null safety (most other languages that provide good
| null safety mechanisms like C#, Kotlin and TypeScript are
| unsound due to reliance on underlying legacy platforms).
| eklavya wrote:
| How is NPE a safety issue?
| tgv wrote:
| Nil dereferencing in those languages doesn't make them
| unsafe. It throws an exception or panics. And Java has some
| none-nil annotation, IIRC.
|
| Still, none of this is relevant to existing software
| written in C. This is not about a rewrite.
|
| And if it were, rust doesn't offer perfect safety, as many
| tasks almost demand unsafe code. Whereas that doesn't
| happen in Go, Scala, etc. Every situation requires its own
| approach.
| commandersaki wrote:
| _We 're essentially continuing to ship faulty software while
| hoping that tooling will protect us from the worst
| consequences._
|
| Yet one way to measure how effective these mitigations and
| countermeasures are working is looking at the cost of the zero
| day market. The trend continues to going upwards in the
| stupidly expensive realm due to needing multiple chains and
| such to attack software. However, I'm not discounting software
| now developed in memory safe language doesn't already
| contribute to this.
|
| Here is one of the references indicating this in the article:
| https://techcrunch.com/2024/04/06/price-of-zero-day-exploits...
| pron wrote:
| > We're essentially continuing to ship faulty software while
| hoping that tooling will protect us from the worst
| consequences.
|
| It is the consequences that give rise to the cost of a bug or
| the value of preventing it.
|
| > Couldn't one argue that containers and virtual machines also
| protect us from exploiting some of these memory safety bugs?
| They provide isolation boundaries that limit the impact of
| exploits, yet we still consider them insufficient alone.
|
| No, that's not the same because sandboxing only limits the
| impact of vulnerabilities to whatever the program is allowed to
| do in the first place, and that is, indeed, insufficient. The
| mechanisms here reduce the impact of vulnerabilities to _less_
| than what the program is allowed to do. To what extent they
| succeed is another matter, but the two are not at all
| comparable.
|
| > The big idea is to shift bug detection to the left: from
| production to development.
|
| This has been the idea behind automated tests since the
| practice first gained popularity. But it's important to
| understand that it works due to a complicated calculus of
| costs. In principle, there are ways to eliminate virtually all
| bugs with various formal methods, yet no one is proposing that
| this should be the primary approach in most situations because
| despite "shifting left" it is not cost effective.
|
| Everyone may pick their language based on their aesthetic
| preference and attraction to certain features, but we should
| avoid sweeping statements about software correctness and the
| best means to achieve it. Once there are costs involved (and
| all languages that prevent certain classes of bugs exact some
| cost) the cost/benefit calculus becomes very complex, with lots
| of variables. Practical software correctness is an extremely
| complicated topic, and it's rare we can make sweeping universal
| statements. What's important is to obtain empirical data and
| study it carefully.
|
| For example, in the 1970s, the prediction by the relevant
| experts was that software would not scale without formal proof.
| Twenty years later, those predictions were proven wrong [1] as
| unsound (i.e. without absolute guarantees) software development
| techniques, such as code review and automated tests, proved far
| more effective than anticipated, while sound techniques proved
| much harder to scale beyond a relatively narrow class of
| program properties.
|
| Note that this paper also makes some empirical claims without
| much evidence, so I take its claims about the effectiveness of
| these approaches with the same scepticism as I do the claims
| about the effectiveness of Rust's approach.
|
| [1]: https://6826.csail.mit.edu/2020/papers/noproof.pdf
| sramsay wrote:
| > Everyone may pick their language based on their aesthetic
| preference and attraction to certain features, but we should
| avoid sweeping statements about software correctness and the
| best means to achieve it. Once there are costs involved (and
| all languages that prevent certain classes of bugs exact some
| cost) the cost/benefit calculus becomes very complex, with
| lots of variables. Practical software correctness is an
| extremely complicated topic, and it's rare we can make
| sweeping universal statements.
|
| Thank you. I feel like this perspective is forever being lost
| in these discussions -- as if gaining the highest possible
| level of assurance with respect to security in a critical
| system were a simple matter of choosing a "safe language" or
| flipping some switch. Or conversely, avoiding languages that
| are "unsafe."
|
| It is never this simple. Never. And when engineers start
| talking this way in particular circumstances, I begin to
| wonder if they really understand the problem at hand.
| awaymazdacx5 wrote:
| C and C++ code is paradigmatic in being susceptible to CLI
| security vulnerabilities.
|
| Object-oriented languages typically work in the set A-Z, with
| limited characters, parameters, etc...
|
| Whereas Wittgenstein's concept of the private language is
| internal and discursive in Skinnerite probabalistic capacities.
| saagarjha wrote:
| There is actually an interesting niche that one can carve out
| when dealing with an attacker who has a memory corruption
| primitive but this paper is a bit too simple to explore that
| space. Preventing RCE is too broad of a goal; attackers on the
| platforms listed continue to bypass implementations of the
| mitigations presented and achieve some form of RCE. The paper
| suggests these are because of implementation issues, and some are
| clearly bugs in the implementation, but many are actually
| completely novel and unaddressed workarounds that require a
| redesign of the mitigation itself. For example, "heap isolation"
| can be done by moving allocations away from each other such that
| a linear overflow will run into a guard page and trap. Is it an
| implementation bug or a fundamental problem that an attacker can
| then poke bytes directly into a target allocation rather than
| linearly overwriting things? Control flow integrity has been
| implemented but attackers then find that, in a large application,
| calling whole functions in a sequence can lead to the results
| they want. Is this a problem with CFI or that specific
| implementation of CFI? One of the reasons that memory safety is
| useful is that it's a lot easier to agree on what it is and how
| to achieve it, and with that what security properties it should
| have. Defining the security properties of mitigations is quite a
| bit harder. That isn't to say that they're not useful, or can't
| be analyzed, but generally the result is not actually denial of
| RCE.
| xianga wrote:
| Not to be harsh, but this article is messy, and I find myself
| agreeing with the commencts regarding hyperboles and AI-writing.
| It's a directionless nothingburger that hasn't really made any
| efforts to look into recent research on memory allocation and
| hardening.
|
| Some examples:
|
| In the heap-section, the article only cites +-20 year old papers
| and mixes user- and kernel-space allocators such as Apple's
| kernel kalloc_type and one for Chrome. Not to mention that the
| author talks about implemention heap regions per-object as if
| that wont introduce significant memory and performance overhead
| and is completely unrealistic in a comodity OS, let alone in a
| server setting.
|
| The pointer integrity section ends with the statement "The above
| protections, in combination, can prevent complete software
| control by attackers able to corrupt memory." - which is not true
| (see recent CVE i found after a quick search [1]), even for
| relativly hardened Apple products that use pointer authentication
| and a type segregated allocator kalloc_type.
|
| Additionally, literally the next sentence in the subsequent
| section contradicts the previous statement (!): "...therefore,
| attackers will still be able to change software behavior by
| corrupting memory". Really dampens the credibility.
|
| If anyone is interested in some recent contributions in the
| space, I've been looking into these recently (in no particular
| order): SeaK [2], ViK[3] and Safeslab [4].
|
| [1] https://www.cvedetails.com/cve/CVE-2025-24085/ [2]
| https://www.usenix.org/conference/usenixsecurity24/presentat...
| [3] https://dl.acm.org/doi/10.1145/3503222.3507780 [4]
| https://dl.acm.org/doi/10.1145/3658644.3670279
| gizmo wrote:
| The memory protection strategies this paper argues for are fine.
| If we can recompile legacy software to gain better protection
| against stack and heap exploits that's a clear win.
|
| As the paper points out memory safety is not a great concern on
| phones because applications are sandboxed. And that's correct. If
| an application is stuck in a sandbox it doesn't matter what that
| process does within its own process space. Smartphones taught us
| what we already knew: process isolation works.
|
| Then the paper observes that memory safety is still a significant
| problem on the server. But instead of pointing out the root cause
| -- the absence of sandboxing -- the authors argue that
| applications should instead be rewritten in go or rust! This is
| absurd. The kernel already provides strong memory protection
| guarantees for each process. The kernel also provides hard
| guarantees for access to devices and the file system. But server
| software doesn't take advantage of any of these guarantees. When
| a server process intermixes data of multiple customers and
| privilege levels then any tiny programming mistake (regardless of
| memory safety) can result in privilege escalation or catastrophic
| data leaks. What use is memory safety when your go program
| returns the wrong user's data because of an off-by-one error? You
| don't need a root exploit if your process already has "root
| access" to the database server.
|
| If we want to be serious about writing secure software on the
| server we have to start taking advantage of the process isolation
| the kernel provides. The kernel can enforce that a web request
| from user A cannot return data from user B because the process
| simply cannot open any files that belong to the wrong user. This
| completely eliminates all memory safety concerns. But today
| software on the server emulates what the kernel already does with
| threading, scheduling, and memory protection, except poorly and
| in userspace and without any hardware guarantees. Effectively all
| code runs as root in ring 0. And we're surprised that security
| continues to plague our industry?
| VWWHFSfQ wrote:
| > memory safety is not a great concern on phones because
| applications are sandboxed. And that's correct. If an
| application is stuck in a sandbox it doesn't matter what that
| process does within its own process space. Smartphones taught
| us what we already knew: process isolation works.
|
| I thought we learned that this doesn't work after the iOS
| 0-click exploit chain running "sandboxed" app code in the
| kernel.
| jchw wrote:
| > Then the paper observes that memory safety is still a
| significant problem on the server. But instead of pointing out
| the root cause -- the absence of sandboxing -- the authors
| argue that applications should instead be rewritten in go or
| rust! This is absurd. The kernel already provides strong memory
| protection guarantees for each process. The kernel also
| provides hard guarantees for access to devices and the file
| system. But server software doesn't take advantage of any of
| these guarantees. When a server process intermixes data of
| multiple customers and privilege levels then any tiny
| programming mistake (regardless of memory safety) can result in
| privilege escalation or catastrophic data leaks. What use is
| memory safety when your go program returns the wrong user's
| data because of an off-by-one error? You don't need a root
| exploit if your process already has "root access" to the
| database server.
|
| Yes, because servers are _inherently_ multi-tenant, you can 't
| _inherently_ avoid the risks. Process isolation _can 't_ help
| you, even if you had the resources to fork off for every single
| request. If you have a database pool in your process, you can
| go and access other people's data. There is never going to be a
| case where having an RCE to a server isn't a serious issue.
|
| Also, neither process isolation nor memory safety can guarantee
| you don't simply return the wrong data for a given customer, so
| that point really neither here nor there.
|
| (I'd also argue that memory safety clearly matters on mobile
| platforms still anyway. Many of the exploit chains that break
| kernel protections still rely on exploiting memory bugs in
| userland first before they can climb their way up. There's also
| other risks to this. Getting an RCE into someone's Signal
| process is an extremely dangerous event for the user.)
| gizmo wrote:
| Databases also have access controls! Which developers don't
| use.
|
| If you have 10.000 tenants on the same server you can simply
| have 10.000 database users. And that simple precaution will
| provide nearly perfect protection against cross-tenant data
| leaks.
| jchw wrote:
| The database was merely an example, there are other shared
| resources that are going to run into the same problem, like
| caches. Still, it's weird to imply that database users are
| _meant_ to map to application users without any kind of
| justification other than "you _can_ do it " but OK, let's
| assume that we can. Let's consider Postgres, which will
| have absolutely no problem creating 10,000 users (not sure
| about 100,000 or 1,000,000, but we can put that aside
| anyhow.) You basically have two potential options here:
|
| - The most logical option is to continue to use database
| pooling as you currently do, and authenticate as a single
| user. Then, when handling a user request, you can
| impersonate a specific database user. Only problem is, if
| you do this, the protection you get is entirely
| discretionary: the connection is still absolutely
| authenticated to a user that _can_ do more, and all you
| have to do is "reset role" and go on your way. So you
| _can_ do this, but it doesn 't help you with server
| exploits.
|
| - The other way to handle this is by having each request
| get a separate database connection which is _actually_
| authenticated to a specific user. That will work and
| provide the database level guarantees. However, for obvious
| reasons, you definitely can 't share a global database pool
| with this approach. That's a problem, because each Postgres
| connection will cost 5-10 MiB or so. If you had 10,000
| active users, you would spend 50-100 GiB on just per-
| connection resources on your database server box. This
| solution scales horribly even when the scale isn't that
| crazy.
|
| And this is all assuming you can actually get the
| guarantees you need just using the database layer. To that
| I say, good luck. You'll basically need to do most of your
| logic in the database instead of the application layer and
| make use of features like row-level security. You _can_ do
| this, but it 's an extremely limiting architecture, not the
| least of which because databases are hard to scale except
| vertically. If you run into any scenario where you outgrow
| a single database cluster, everything here goes out the
| window.
|
| Needless to say, nobody does this, and they're totally
| right. Having a database assist in things like
| authorization and visibility is not a terrible idea or
| anything, but all of this taken together is just not very
| persuasive.
|
| And besides. Postgres itself, along with basically all of
| the other major databases, are also not necessarily memory-
| safe. Having external parties have access to your database
| connection pretty much puts you back at square one for
| defenses against potential unknown memory safety bugs,
| making this entire exercise a bit pointless...
| gizmo wrote:
| Most server software today intermingles data that should
| be strictly isolated because it's _convenient_. I don 't
| buy your arguments about efficiency either, because
| despite having no data isolation web software is still
| comically (tragically) slow.
| jchw wrote:
| Three things, very briefly:
|
| - Scaling is different than efficiency.
|
| - Even if you can afford to eat CPU costs, memory
| limitations are inflexible.
|
| - Again, this moves the problem to the database. The
| database is still written in C, and still deals with
| multi-tenant workloads in the same process, and you
| assume your application server _might_ get RCE 'd, which
| means now someone has a connection to your database,
| already authenticated, and it may very well have memory
| safety or other bugs. You can't escape this problem by
| moving it.
| graemep wrote:
| > Also, neither process isolation nor memory safety can
| guarantee you don't simply return the wrong data for a given
| customer, so that point really neither here nor there.
|
| The fact that there can be security issues at the application
| level is no reason to add memory safety issues to them!
|
| You may well have the resources to fork a process per
| customer, not have a database pool, not cache etc. its a
| trade-off with resources needed and performance.
|
| > Also, neither process isolation nor memory safety can
| guarantee you don't simply return the wrong data for a given
| customer, so that point really neither here nor there.
|
| You should fix the problems you can, even if there are
| problems you cannot.
| jchw wrote:
| > The fact that there can be security issues at the
| application level is no reason to add memory safety issues
| to them!
|
| > You may well have the resources to fork a process per
| customer, not have a database pool, not cache etc. its a
| trade-off with resources needed and performance.
|
| This feels like one of those weird things that wind up
| happening in discussions about a lot of things, including
| say, renewable energy. People get so hung up about certain
| details that the discussion winds up going into a direction
| that really doesn't make sense, and it could only go there
| incrementally because it would've been very obvious if you
| followed the entire chain.
|
| Please bear with me. Consider the primary problem in the
| first place:
|
| _The problem is that we have a bunch of things that are
| not memory safe, already. Rewriting all of those things is
| going to take a lot of time and effort._
|
| All of these ideas about isolating requests are totally
| fine, honestly. If you can afford those sorts of trade-offs
| in your design, then go hog wild. I find many of these
| architectural decisions to be a bit pointless for reasons
| I'm happy to dig into in more detail if people really care,
| but the important thing to understand is that I'm not
| telling you to _not_ try to isolate requests. I 'm just
| saying that in practice, there are no "legacy" servers that
| apply this degree of request isolation. They were
| architected with the assumption that the process would not
| get RCE'd, and therefore it would require a full
| rearchitecting to make them safe in the face of that sort
| of bug.
|
| But when we talk about architectures where we avoid
| connection pooling and enforce security policy entirely in
| the database layer, that essentially means _writing new
| servers_. And if you 're writing new servers, why in the
| world would you go through all of this effort when you
| don't have to? I'll grant you that the Rust borrow checker
| has a decently steep learning curve, but I don't expect it
| to be a serious barrier to experienced C++ programmers if
| they are really trying to learn and not just looking for
| reasons to not have to. C++ is not an easy language,
| either.
|
| It would be absolutely insane to perform all of this per-
| request isolation voodoo and then _not_ start on a clean
| slate with a memory safe language in the first place. You
| may as well take _both_ at that point. Fuck it, run each
| request inside a small KVM context! And in fact, you can do
| that. Microsoft did that[1], but note that even in their
| press release for Hyperlight, they are showing it being
| used in Rust code, because that 's the logical order of
| operations: If you're truly serious about security, and
| you're building something from scratch, start by preventing
| vulnerabilities as far left in the process as possible; and
| you can't go further left than having a programming
| language that can prevent certain bugs entirely by-design.
|
| > You should fix the problems you can, even if there are
| problems you cannot.
|
| That goes both ways.
|
| [1]: https://opensource.microsoft.com/blog/2024/11/07/intro
| ducing...
| graemep wrote:
| > All of these ideas about isolating requests are totally
| fine, honestly. If you can afford those sorts of trade-
| offs in your design, then go hog wild.
|
| I think far fewer people make some of these trade offs
| than could afford to.
|
| I think there is a more fundamental problem with
| databases and data in general. A lot of what servers do
| involves shared data - think of the typical CRUD app
| where multiple people can read or modify the same data,
| and that is the valuable data.
|
| > If you're truly serious about security, and you're
| building something from scratch, start by preventing
| vulnerabilities as far left in the process as possible;
| and you can't go further left than having a programming
| language that can prevent certain bugs entirely by-
| design.
|
| Agreed.
| deepsun wrote:
| > doesn't matter what that process does within its own process
| space
|
| Re. phones -- you assume that a process hacks another process.
| But a there might be vulnerability within the process itself,
| corrupting its own memory. Sandboxing doesn't help.
| gizmo wrote:
| What matters is that a random app cannot access sensitive
| data like your passwords, sessions, email. On iOS you can run
| anything from the app store and it's fine. On Windows any
| .exe you run can cause havoc.
| deepsun wrote:
| My point that memory corruption can happen in the password
| app itself, not in a random app.
| gizmo wrote:
| Who cares if my password app has memory corruption? There
| are no negative consequences. Worst case I have to re-
| download my password database file.
| IshKebab wrote:
| Good job sandbox escapes and local root exploits never exist!
| gizmo wrote:
| 1. The point is you don't need root when the unprivileged
| process already has access to all data. There is nothing more
| to be gained.
|
| 2. Good luck breaking out of your AWS instance into the
| hypervisor.
| VWWHFSfQ wrote:
| > 2. Good luck breaking out of your AWS instance into the
| hypervisor.
|
| It doesn't take luck. Just skill, resources, and
| motivation. Like we already saw happen to the iOS
| "sandbox".
| IshKebab wrote:
| > Good luck breaking out of your AWS instance into the
| hypervisor.
|
| https://www.itnews.com.au/news/xen-patches-critical-guest-
| pr...
| pornel wrote:
| The kernel knows about system-local users, but not the remote
| ones. Servers may need to access data of multiple users at
| once, so it's not as simple as some setuid+chroot CGI for every
| cookie received. Kernels like Linux are not designed for that.
|
| Maybe it would be more feasible with some capability-based
| kernel, but you'd inherently have a lot of logic around user
| accounts, privileges, and queries. You end up involving the
| kernel in what is row-level database security. That adds a lot
| of complexity to the kernel, which also makes the isolation
| itself have more of the attack surface.
|
| OTOH you can write your logic in a memory-safe language today.
| The VM/runtime/guaranteed-safe-subset is your "kernel" that
| protects the process from getting hijacked -- an off-by-one
| error can't cause arbitrary code execution. The VM/runtime
| itself can still have vulnerabilities, but that just becomes
| analogous to kernel vulnerabilities.
| vacuity wrote:
| > Maybe it would be more feasible with some capability-based
| kernel, but you'd inherently have a lot of logic around user
| accounts, privileges, and queries. You end up involving the
| kernel in what is row-level database security. That adds a
| lot of complexity to the kernel, which also makes the
| isolation itself have more of the attack surface.
|
| Microkernels/exokernels sacrificing some performance to bring
| reliable kernels that allow for reliable userspace.
| MisterTea wrote:
| > That adds a lot of complexity to the kernel, which also
| makes the isolation itself have more of the attack surface.
|
| Not if you remove auth from the kernel:
| https://doc.cat-v.org/plan_9/4th_edition/papers/auth The Plan
| 9 kernel is very small and portable which demonstrates that
| you don't need complexity to do distributed auth properly.
| The current OS hegemony is incredibly dated design wise
| because their kernels were all designed to run on a single
| machine.
|
| > OTOH you can write your logic in a memory-safe language
| today.
|
| Memory safety is not security.
| tonyhart7 wrote:
| commenting before reading it but I guess memory arena???
| pizlonator wrote:
| I think this paper overestimates the benefit of what I call
| isoheaps (partitioning allocations by type). I wrote the WebKit
| isoheap implementation so it's something I care about a lot.
|
| Isoheaps can make mostly neutralize use after free bugs. But
| that's all they do. Moreover they don't scale well. If you
| isoheap a select set of stuff then it's fine, but if you tried to
| deploy isoheaps to every allocation you get massive memory
| overhead (2x or more) and substantial time overhead too. I know
| because I tried that.
|
| If an attacker finds a type confusion or heap buffer overflow
| then isoheaps won't prevent the attacker from controlling heap
| layout. All it takes is that they can confuse an int with a ptr
| and game over. If they can read ptr values as ints then they can
| figure out how the heap is laid out (no matter how weirdly you
| laid it out). If they also can write ptr values as ints then
| control they whole heap. At that point it doesn't even matter if
| you have control flow integrity.
|
| To defeat attackers you really need some kind of 100% solution
| where you can prove that the attacker can't use a bug with one
| pointer access to control the whole heap.
| Yoric wrote:
| Yes, having coded quite a few years in C++ (on the Firefox
| codebase) before migrating to Rust, I believe that many C and
| C++ developers mistakenly assume that
|
| 1/ memory safety is the unachievable holy grail of safety ;
|
| 2/ there is a magic bullet somewhere that can bring about the
| benefits of memory safety, without any of the costs (real or
| expected).
|
| In practice, the first assumption is wrong because memory-
| safety is just where safety _starts_. Once you have memory-
| safety and type-safety, you can start building stuff. If you
| have already expended all your cognitive budget on reaching
| this point, you have lost.
|
| As for the magic bullets, all those I've seen suggested are of
| the better-than-nothing variety rather than the it-just-works
| variety they're often touted as. Doesn't mean that there won't
| ever be a solution, but I'm not holding my breath.
|
| And of course, I've seen people claim more than once that AI
| will solve code safety & security. So far, that's not quite
| what's written on the wall.
| tzs wrote:
| OT: Are there any memory safe languages that that are fast and
| support goto?
|
| I'm writing something that needs to implement some tax
| computations and I want to implement them to follow as closely as
| possible the forms that are used to report those computations to
| the government. That way it is easy to be sure they are correct
| and easy to update them if the rules change.
|
| The way those forms work is something like this:
| 1. Enter your Foo: _________ 2. Enter your Bar: _________
| 3. Add line 1 and line 2: ________ 4: Enter your Spam:
| _______ 5: Enter the smaller of line 1 and 4: _____
| 6: If line 5 is less than $1000 skip to line 9 7: Enter the
| smaller of line 2 and $5000: _____ 8: If line 7 is greater
| than line 4 skip to 13 ...
|
| With goto you can write code that exactly follows the form:
| Line1: L1 = Foo; Line2: L2 = Bar; Line3: L3 = L1 +
| L2; Line4: L4 = Spam; Line5: L5 = min(L1, L4);
| Line6: if (L5 < 1000) goto Line9; Line7: L6 = min(L2,
| 5000); Line8: if (L7 > L4) goto Line13; ...
|
| For some forms an if (X) goto Y ....
| Y:
|
| can be replaced by if (!X) { ... }
|
| because nothing before that has a goto into the body of the if
| statement. But some forms do have things jumping into places like
| that. Also jumping out of what would be such a body into the body
| of something later.
|
| Writing those without goto tends to require duplicating code. The
| duplication in the source code could be eliminated with a macro
| system but don't most memory safe languages also frown on macro
| systems?
|
| Putting the duplicate code in separate functions could also work
| but often those sections of code refer to things earlier in the
| form so some of the functions might need a lot of arguments.
| However the code then doesn't look much like the paper form so it
| is harder to see that it is correct or to update it when the form
| changes in different years.
| returningfory2 wrote:
| Maybe you could build a domain-specific language.
| steveklabnik wrote:
| > OT: Are there any memory safe languages that that are fast
| and support goto?
|
| Irreducible control flow is a pain for static analysis.
|
| > Writing those without goto tends to require duplicating code
|
| This feels like a regular old state machine to me, which
| obviously is nice to write with goto, but isn't required.
| ahoka wrote:
| Scheme: https://docs.scheme.org/schintro/schintro_141.html
| linux_security wrote:
| Was discussing this paper with a few colleagues who work in this
| area, and concluded that this paper seems like an odd combination
| of:
|
| - The author citing their own research. (Ok, all researchers do
| this) - Mildly scolding the industry for not having applied their
| research. It's "pragmatic" after all.
|
| The elephant in the room is that these approaches have been
| widely deployed and their track record is pretty questionable.
| iPhone widely deploys PAC and kalloc_type. Chrome applies CFI and
| PartitionAlloc. Android applies CFI and Scudo. Yet memory safety
| exploitation still regularly occurs against these targets. Is it
| harder because of these technologies? Probably. But if they're so
| effective, why are attackers still regularly successful at
| exploiting memory safety bugs? And what's the cost of applying
| these? Does my phone's battery die sooner? Is it slower? So now
| your phone/browser are slower AND still exploitable.
| comex wrote:
| The paper significantly overstates the scope of PAC (pointer
| authentication codes) on Apple platforms. To quote the paper:
|
| > This is effectively what is done in Apple software, which uses
| special ARM hardware support to also check pointer integrity at
| runtime--i.e., ensure each pointer access uses pointers of the
| right type[]. Apple uses this further to enforce a form of stack
| integrity, control-flow integrity, and heap integrity
|
| In reality, the compiler only automatically applies PAC to _code_
| pointers: stack return addresses, function pointers, and C++
| vtable method pointers. You can also manually apply PAC to other
| pointers using attributes and intrinsics; this is used by
| components like the Objective-C runtime and the memory allocator.
| But only a tiny amount of code does this.
|
| PAC is nevertheless a powerful mitigation. But let's see how it
| stacks up against the paper's claims:
|
| - Heap integrity:
|
| Since Apple's implementation of PAC leaves most data pointers
| unsigned, it has little direct relevance to heap integrity. The
| paper seems to want to sign _all_ pointers. You could
| theoretically implement a compiler feature to use PAC
| instructions for all pointers, but I don't think anyone (not just
| Apple) has implemented such a thing. It would probably come with
| high performance and compatibility costs.
|
| - Stack integrity:
|
| The paper defines this as "attackers cannot change the arguments,
| local variables, or return sites". PAC makes it difficult to
| change return sites, but does nothing to prevent changing
| arguments and local variables (unless you're limited to a linear
| overwrite). Again, it's theoretically possible to use PAC
| instructions to secure those things: there is a technique to make
| a single signature that combines multiple pointer-sized values,
| so you could try to make one signature that covers the whole set
| of local variables and other stack bits. But nobody does this, so
| the compatibility and performance costs are unknown. Even
| SafeStack (which the paper also cites) does not fully protect
| local variables, though it gets closer.
|
| - Control-flow integrity:
|
| The paper mentions "type signatures", but Apple's PAC-based CFI
| does not validate type signatures for C function pointers, only
| vtables and Objective-C isa pointers. Other CFI implementations
| do validate C function pointer type signatures, like Android,
| though this seems to come at the cost of a slower pace of
| adoption.
|
| More importantly, attackers have demonstrated the ability to get
| around CFI, by substituting valid but irrelevant function
| pointers to achieve "jump-oriented programming" (JOP). Project
| Zero recently published a blog post explaining a (semi-recent)
| iOS exploit that used this technique:
|
| https://googleprojectzero.blogspot.com/2025/03/blasting-past...
|
| I'm not sure whether type signature validation would have
| prevented this particular exploit, but many C function pointers
| have pretty simple signatures (yet may do wildly different
| things), so the benefit is somewhat limited.
| nickpsecurity wrote:
| I've always favored a large public/private investment into open-
| source tools like Coverity, PVS Check, and RV-Match. Put extra
| effort into suppressing false positives and autofixing simple
| problems. Companies like Apple had enough money to straight up
| buy the vendors of these tools.
|
| I'd also say, like CPAChecker and Why3, they should be designed
| in a flexible way where different languages can easily be added.
| Also, new passes for analyzers. Then, just keep running it on all
| the C/C++ code in low, false, positive mode.
|
| On top of this, there have been techniques to efficiently do
| total memory safety. Softbound + CETS was an example. We should
| invest in more of those techniques. Then, combine the analyzers
| with those tools to only do runtime checks on what couldn't be
| proven.
___________________________________________________________________
(page generated 2025-03-31 23:01 UTC)