2

Software libraries targetting resource constrained environments like embedded systems use conditional compilation to allow consumers to shave space and thus increase performance by removing unused features from the final binaries distributed in production.

Assume that library developers produced the compiler flags and were a consideration at design and test phases of the library.

As most design decisions, there are tradeoffs, in this case the code complexity and product quality unarguably suffer due to the increased branches to design and test against.

However with regards to security, the net impact is not clear, there are both positive and negative effects from removing features. Initially, removing code reduces the surface of attack. But on the other hand, building a custom binary means that a bug, and thus exploit, might be present in that specific combination.

The implications differ from those of traditional runtime path complexity, not just because of the consideration of 2 different types of branching, but because compiler condition syntax is far unsafer than its runtime counterpart (at least in C).

An interesting phenomenon is that building a custom binary might expose the users to targetted attacks, but using a standard build might expose the users to mass exploits.

The question is, considering there are both positive and negative influences on security, if we were to quantify them, would the net impact be positive or negative? In other, less academic words, if one is concerned with security, should they build a custom binary without the features they need?

The specific example that sparked this question is Busybox heavy use of conditionally compiled feature flags. https://git.busybox.net/busybox/tree/networking/httpd.c

2
2

In this case I would consider the custom binary (if any) to be safer, due to the smaller surface of attack.

The main problem I would worry about "removing code" would be unintended side-effects, particularly when the code is being removed by a third party, not too familiar with the project.

In this case, these are conditional flags officially supported by upstream. And despite the large number of flags, they look sensible, encapsulating one feature each.

I don't think your statement that "compiler condition syntax is far unsafer than its runtime counterpart" is relevant here.

building a custom binary might expose the users to targetted attacks, but using a standard build might expose the users to mass exploits

Using a standard build you would still be exposed to targetted attacks, in addition to mass exploits. The disadvantage would be that your custom build could somehow be vulnerable although the normal build was not, perhaps because the specific combination used was unsafe (e.g. suppose that it let you remove an check that was necessary), this would still be a bug in the code, since these are options offered by upstream.

Or perhaps because there was a vulnerability, but some of the other cruft made it non-exploitable (or hard to exploit), and removing a piece of code you made yourself vulnerable to it (for example, a buffer overflow could be overwriting an unused buffer in the standard binary, but overwriting the stack in your build).

However, it could as well be the opposite case that your build ends up being safe with the standard one unsafe. Those would mostly even out, so I would consider the smaller surface to "win" in general.

A different issue, which I think is more important, is maintainability. So you are now designing a new (IoT?) system, and are deciding whether to use a custom build of busybox or using a prebuilt binary (I am leaving aside the potential issues of trusting the provided binary). Should you use a custom build, would that make it harder to update later, compared with copying a standard release? The main risk will come from future vulnerabilities not being patched in your release. If using a custom build will lead to the program not being updated in three years, but every quarter if using upstream binaries, the later would be much safer. (Sadly, the real world answer often is that the binaries will never be updated, in either way)

0

I think if you're distributing all the various different permutations one way or another, you're still responsible for attacks against the whole application overall. While users of a single combination might be safer because of the reduced attack surface, you still have the same number of potential problems overall across all the permutations. If the cost to have this is reduced testing, I would say you're worse off overall.

The other concern is ease of issuing updates when problems are found. If having complicated different builds makes distributing patches more complicated, I think that pushes to negative direction as well.

In other words, I would take a better tested larger application, over a smaller, but worse tested application.

With that said, I think the devil is really in the details of specific examples. If your application makes it easy to automate testing, and thus reduces the testing cost for different builds, maybe that pushes you in the direction of multiple options.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.