April 25th, 2024 • Buildable News & Resources

Last week we discussed what memory safety is and looked at the impacts of using non-memory safe languages. In this second part of our two-part blog series we will offer insights into the evolving landscape of memory safety and its implications for the future. We will explore how to achieve memory safety, discuss the adoption of memory-safe languages, and highlight mechanisms for reducing memory safety issues. 

Why Aren’t we Memory Safe Already?  

It’s not an easy task to get rid of non-memory safe software programming which has been relied on since the 1970s and which has been used to build all the low-level functionality of the systems we use today. Many critical applications and software infrastructures rely on these non-memory safe languages. Removing them entirely means rewriting large portions of software, which at this point may not be humanly feasible.   

Up until a few decades ago, almost all system software was written in C or C++, both non-memory safe languages. Their use has been greatly reduced in the past few years; thanks to Java, .NET, and other languages; however, they are still heavily relied on. According to Statista, in 2023 about 41.76% of software developers were using C++ or C. There is a reason for this continued reliance, as there are some significant advantages for using C or C++. For example, C++ is more efficient than C#: it’s very fast; has a small memory and disk footprint; is mature, predictable, and has a highly applicable platform; and it does not need additional installed components to run. 

Most of the advantages of using non-memory safe languages lie in execution speed and size of executables, which is why they are still being used despite the security risks. A lower-level language will simply be faster when it’s relying on a non-memory safe language. Higher level languages such as Java, .NET, and JavaScript are interpreted languages and need a VM (Virtual Machine) to execute code, which provides a layer of abstraction and memory safety for the programmer, but which may also slow down the system. 

Non-memory safe programming also allows for the precise control and predictability needed for low-level optimizations, such as kernel development and embedded systems programming.  

In certain situations, it may be necessary to use a non-memory safe language. Examples of this scenario might include when working with drivers or embedded software or when interacting directly with hardware components.  

Some other reasons why memory-safe languages may not be implemented include:  

  • Distrust for memory safe languages/resistance to change 
  • ‘If it’s not broke, don’t fix it.’ attitude 
  • Legacy systems may not be able to adapt 
  • Costly to rewrite new code, retrain developers 
  • Aging programmers that are resistant to learning new programs 

It’s also important to remember that memory-safe languages are not perfect and may introduce safety latencies of their own that could reduce system performance. 

Becoming Memory Safe with Safe Languages 

The most obvious and effective way to be memory safe when creating new programs is to exclusively use memory safe languages from the start. The White House published a report earlier this month, titled Back to the Building Blocks: A Path Toward Secure and Measurable Software. In it, the National Security Agency (NSA), pushes the use of memory safe programming languages, “The highest leverage method to reduce memory safety vulnerabilities is to secure one of the building blocks of cyberspace: the programming language.” 

Some of the more popular memory-safe languages include C#, Java, Typescript, Ruby, Python, Rust, Swift, Kotlin, and Go.  

When working to expand or alter already established software written in a non-memory safe language, we may be able to rewrite the entire program so that it’s memory safe. This will likely take significant time and resources and will also require training developers or hiring new ones. 

Programmers can write new modules for an existing code base in a memory-safe language, incrementally and gradually changing over to a completely memory-safe system. This may require building data structures to allow for data exchange between the two languages until the whole system is upgraded to be memory safe. 

If the system is too complicated or widespread, is a legacy system, or is built on top of a legacy system it may not be feasible to simply rewrite the entire program. In this case, we may use a more gradual, methodical approach.   

In ‘The Case for Memory Secure Roadmaps: Why Both C-Suite Executives and Technical Experts Need to Take Memory Safe Coding Seriously’, published by the United States Cybersecurity and Infrastructure Security Agency and other government agencies, they provide helpful guidance for manufacturers with steps to implement changes for eliminating memory safety vulnerabilities from their products. They recommend the following: 

  • Start with new and smaller projects that carry low risk to give development teams experience with new tools and processes. 
  • Replace memory unsafe, self-contained components individually.  
  • Prioritize security-critical code that may be sensitive from a security perspective or located along a critical attack path. 
  • Use instrumentation to detect memory errors. 
  • Evaluate performance and complexity to rewrite smaller components or larger systems. 
  • Determine which modules are CPU-bound (experiencing more performance fluctuations). 
  • Ramp up parallel systems to add new codebase in small portions incrementally until the old system is completely phased out.  
  • Wrap applications, in which an intermediary application for public interfaces is used, to ensure all inputs cannot exceed memory bounds within the new application. 

Becoming Memory Safe Through Other Methods  

Although implementing memory-safe languages is the most obvious method, memory safety can be achieved through other methods, or a combination of methods, to create a comprehensive memory safety strategy.  

These methods might include: 

  • careful management of memory allocation and deallocation; 
  • robust error checking and code review; 
  • fuzzers and sanitizers to help find bugs before they become a problem;  
  • static and dynamic analysis tools to find and fix security threats; 
  • techniques such as bounds checking and sandboxing to isolate unsafe code and decrease potential harm; 
  • and continuous security checks.  

Programmers can also take advantage of application programming interfaces (API’s), so that a whole new language doesn’t need to be learned. In fact, a lot of Rust components come equipped with C API. 

 

What Now?  

Given the exponential growth of operating systems and the explosion of Internet of Things (IoT) devices, the imperative to explore memory-safe alternatives is becoming increasingly important. There is a global-wide push to switch to memory-safe programming languages, especially as government entities make it a priority.  

In the White House report mentioned above responsibility is put on the programmers themselves, “Programmers writing lines of code do not do so without consequence; the way they do their work is of critical importance to the national interest.” This pressure will likely increase as more guidelines are presented and perhaps even regulations established to ensure forward movement for memory-safe systems. 

Many are already well on their way toward achieving memory safety. Google reports that as they have reduced the amount of unsafe-memory coding in their Androids, memory safe vulnerabilities have also been reduced. They state that from 2019 to 2022 the percentage of their vulnerabilities that were caused by memory safety issues had decreased from 76% to 35%. 2022 was the first year that memory safety vulnerabilities did not represent the majority of Android’s vulnerabilities. 

Other recent strategies for memory-safe systems have included:  

  • Meta is investing in Rust and has even joined the non-profit Rust Foundation; 
  • Linux has added support for Rust in kernel development;  
  • about 1/5th of all new native code for the Newer Google Android 13 OS, is written in Rust; 
  • and Google, Mozilla, ISRG Prossimo, and Rust all now offer open source memory-safe code. 

At Buildable, we primarily use C#, Typescript, Python, and Java/Swift for Android/iOS specific cases. It is a conscious choice. We have 20 years of experience with C#, and JavaScript. As such, we have seen these languages evolve over time and appreciate the increasing standard that they impose upon programmers after each new release. We put an onus on thread-safety, memory-safety, and execution-safety to shelter the software we write from vulnerabilities that could emanate from mundane tasks. 

 

By prioritizing memory safety in software development practices and leveraging the right tools and methodologies, organizations can mitigate the risk of memory-related vulnerabilities and build more resilient and trustworthy software ecosystems. Customers, when given a choice, will choose the safer option, propelling us further into a memory-safe future.  

Ready to work with us?

Request a quote for your next project

Let's talk

Buildable's Logo 1-Color

What can we help you with?

Talk with an expert at Buildable about your project.

 
 

This site is protected by reCAPTCHA. Google Privacy Policy and Terms of Service apply.

Copyright © 2024 Buildable.
All Rights Reserved
Privacy Policy | Terms of Service

Web Design and Web Development by Buildable