3 things that make logging fast and secure
Log4J is making the news because of recent major security vulnerabilities. Writing secure software is extremely hard, and logging frameworks are often overlooked as a simple, unimportant component.
Any software component that parses and outputs user input needs to be considered seriously from a security standpoint.
Being written in C++, Quasar does not use log4j. It does not use or any off-the-shelve components, either.
Quasar has its bespoke log engine. Sounds crazy?
Here we explore three characteristics that make it secure and fast.
It’s asynchronous
Asynchronous logging is a must for high-performance applications. When Quasar logs something, the actual logging occurs in a dedicated thread that will not block the execution flow.
For example, if we take this piece of code:
for (const auto & [uid, v] : values_map) { // force the type to std::string to avoid having the data // in a view const std::string key_name = uid.empty() ? default_key_name : fmt::format("{}.uid_{}", k, uid.value()); const auto ec = store_locally(key_name, v); if (ec) { log::emit(log::warning, "could not store local key {}: {}", key_name, ec.message()); } }
When the log is emitted, the string is formatted into a log packet. The log packet is then pushed down a lock-free asynchronous queue. A background thread will read from the asynchronous line and write the content on the configurable sink (console, event log, file…).
Logging will thus never result in waiting for I/O, and the performance impact is minimal.
Tight memory management
Log packets used for logging are pre-allocated on startup, reducing the performance penalty of logging. But it’s not just a performance gain; it’s also a security advantage.
The loglines are formatted so that if they exceed the packet size, they will be trimmed down. This size limit prevents log trashing and seriously limits the potential for memory overruns.
In addition to that, log packets exist in a separate memory area with fences. If a memory overrun occurs despite trimming, it will be seriously mitigated.
Compile-time format checks
Let’s look at our piece of code again:
log::emit(log::warning, "could not store local key {}: {}", key_name, ec.message());
As you can see, we don’t use printf specifiers. If you’re into having your software listed on 0-days exploit sites regularly, please use printf and sprintf as much as you can.
But you know, if you’re a true masochist, keep in mind that Quasar is always hiring great software engineers. We promise that your suffering will be legendary.
Quasar uses libfmt to format log lines, with compile-time formatting checks in place. Not only does libfmt dramatically reduce the chances of a format error, but it will tell you, at compilation, if you’ve made a mistake.
Last but not least, libfmt is extremely fast. That’s good because we don’t want to waste precious CPU cycles on logging.
Quasar and security
While Quasar is mainly known for being high performance, we seriously take security. We have taken a lot of care into details such as logging to increase the robustness and safety of Quasar.
Writing secure software is extremely hard. Security is a never-ending process.
Curious about what Quasar is? Learn more here!
Want to take Quasar for a spin? Try our free community edition!