In recent years, the term ‘memory safety’ has been increasingly put in the spotlight, as it is a critical component in safeguarding against cyber threats and protecting sensitive data. We live in an interconnected world where software vulnerabilities can have far-reaching consequences. Creating memory safe systems has become a shared goal around the world.  

Memory safety is crucial in software development because it ensures that programs operate reliably and securely. When memory safety is compromised, it can lead to a variety of issues such as crashes, unpredictable behavior, and security vulnerabilities. By enforcing memory safety, developers can minimize the risk of these problems, enhancing the stability, security, and overall quality of their software applications. 

In this first part of our two-part blog, we delve into what memory safety is, the impacts of using non-memory safe languages, and why this issue is so prevalent today. 

What Does Memory Safety Really Mean? 

At its core, memory safety refers to keeping data (including passwords, SSNs, credit card information, and other sensitive data) safe from unauthorized access by other programs. Memory safe programming prevents memory leaks, inhibits access to non-authorized memory segments, and blocks malicious manipulation of memory from attackers. Memory safe programs inhibit injections through arbitrary code input and prevent users from supplying executable code as data input.   

Ensuring memory safety primarily requires using memory safe languages used for programming, such as Rust, Go, C#, Java, Ruby, Swift, Python, and JavaScript. These programming languages rely on built-in, automatic memory management and only allow for safe reads and writes (authorized access to view, open, and alter RAM). They free memory when it is no longer needed, preventing it from being stored unnecessarily and incorrectly. They also prevent buffer overflows, which are the most common security vulnerability exploited by attackers.  

Non-memory safe languages, such as C, C++, and Assembly, require engineers to manually manage memory allocations, a slow and tricky process that is often not completed correctly. When a program’s memory is mismanaged, it can lead to poor performance, application failure, and increased vulnerability. Non-memory safe programs often rely on other libraries, which themselves may be subject to vulnerabilities. This is a common cause of vulnerability that requires patching by software engineers.  

Examples of common errors caused by non-memory safe software include: 

Buffer overflows: In this common scenario, an attacker overwrites data in a buffer (overflow) with carefully crafted binary data designed to be too large for the size of the buffer, which causes the data to spill over on the stack. This eventually overwrites the return address of the function, which in turn allows the attacker to execute the code they provided in the binary input. At this point, the attacker gains control of the user process. This is particularly dangerous when the process attacked is a system process running with elevated privileges. Since most operating systems are written in C and C++, this is a common security vulnerability. 

Out of bounds reads and writes: These errors occur when a program is allowed to open, view, and alter memory that is outside the bounds of what it is authorized to access. For example, a program could try to gain access to another program’s data in RAM or memory mapped to devices in ROM. This isn’t necessarily an issue on modern operating systems which provide memory access safety mechanisms, but it can be a problem on embedded devices which are found everywhere, such as routers, printers, and IoT. These devices may not have enough space to run a full-fledged operating system. 

Use-after free: This is when a (de-referenced) pointer continues to point at a memory that has been deallocated, allowing the user to read data even though it has been deleted. While this manifests itself as a bug and a crash, it can be exploited to read other parts of memory. 

Double-free: This error occurs when the free() function (used to release memory blocks in C programming languages) is used incorrectly. When free() is called more than once with the same memory address as an argument, it can cause the program’s memory management data structure to become corrupted. This might allow a malicious user to write values in random memory locations. Double-free instances can also cause a program to crash or change the order of the software’s executions. 

A Bit of History 

Before the mid 1990s, C, C++, PASCAL, COBOL, Fortran and Assembly languages used to be the languages of choice for building software applications. These languages were popular, since they offered higher-level, object-oriented mechanisms and favored code re-use with their functional approach. The Assembly language is the fastest of all languages but is extremely error prone and is far from being user friendly. This language was very popular when memory was sparse. (Remember in the mid 1980’s most personal computers had less than 512KB of RAM.) COBOL was at the heart of IBM mainframes and is still in use today. C and C++ gained popularity with their novel compact syntax in the mid 1970s and were considered almost as fast as Fortran.  

In the mid 1990s, Java was introduced to the world. It was controversial because it introduced memory safe practices at a time when programmers were used to managing memory themselves. At the time, Java code would not run natively on the target CPU but instead would have to be executed by a Java virtual machine (JVM), which caused programs written in Java to be slow. These factors led to a slow adoption of Java. In 2000, Microsoft invented C#, a distant cousin of Java, but with added language functionality that made it popular. The C# language evolved over time and is now considered a very safe language.  

JavaScript was invented in 1995 as a scripting language used by early web browsers (Netscape) and became widely used. Typescript, a superset of JavaScript was made very popular in 2012 and provided JavaScript with the higher-level, object-oriented mechanism that JavaScript was lacking. Today, Typescript is widely used for both client-side and server-side applications. It also runs within a VM to interpret the JavaScript code. This abstraction provides memory safety to the programmer who consequently does not need to manage memory allocation or the lifetime of the objects being consumed.   

Meanwhile, the C/C++ community has continued to grow. Programs written in C/C++ remain the standard for speed and efficiency (ex: the popular web server nginx is written entirely in C). Programmers have created layers, libraries, and other mechanisms, which have helped to make C/C++ codes safer, but they are still not entirely memory safe. 

Impacts of Non-Memory Safe Programming 

The ramifications of non-memory-safe programming can be severe, ranging from system crashes and data corruption to remote code execution and security breaches. Vulnerabilities stemming from memory safety issues are a favorite target for cyber attackers, who exploit them to gain unauthorized access to data, steal sensitive information, deploy ransomware, and disrupt or deny critical services.  

The use of non-memory safe programs is common and is to blame for most software safety issues. In The Case for Memory Secure RoadmapsWhy 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 these statistics: 

  • About 70% of Microsoft’s common vulnerabilities and exposures (CVEs) are memory safety vulnerabilities (based on 2006-2018 CVEs). 
  • Approximately 70% of vulnerabilities identified in Google’s Chromium project are memory safety vulnerabilities. 
  • In an analysis of Mozilla vulnerabilities, 32 of 34 critical/high bugs were memory safety vulnerabilities. 
  • Based on analysis by Google’s Project Zero team, 67% of zero-day vulnerabilities in 2021 were memory safety vulnerabilities. 

The problems associated with non-memory safe programs can potentially affect anyone, from individuals to large corporations and governments – and are a global concern. Some of the more noteworthy attacks that underscore the real-world consequences of overlooking memory safety have included: 

  • The Slammer worm from 2003 was made possible by a buffer overflow in Microsoft SQL Server. When it came out it was the fastest computer worm in history, doubling in size every 8.5 seconds. The worm infected at least 75,000 hosts, created network outages, caused the cancelation of airline flights, interfered with elections, and generated ATM failures. 
  • The WannaCry (out-of-bounds write) ransomware attack in 2017 spread to about 230,000 computers (operating Microsoft Windows) throughout 150 countries, and cost about $4 billion globally. Users’ files were held hostage until a ransom, in the form of Bitcoin, was paid.   
  • HeartBleed was initiated by a memory safety problem (out-of-bounds read) that was traced to a single line of code in Open SSL. Released in 2014, HeartBleed affected 17% of all SSL servers and caused a security crisis. It allowed malicious users to easily trick vulnerable web servers into sharing sensitive information, including usernames and passwords.  
  • Stagefright was developed (with the help of an out-of-bounds write) in 2015 to attack any Android smartphone, tablet, or other device running Android 2.2 or higher. The virus infected an Android device through a multi-media text message, which in some instances didn’t need to be viewed or even opened to launch. After the worm contaminated the device, it would send a copy of itself to everyone in the person’s contact list.  

Besides being a security risk, non-memory safe software tends to be less stable, allowing for more bugs and crashes. Also, bugs in non-memory safe programs can be difficult to track down, since memory corruption can cause crashes that occur far from where the bug is located. It can take programmers months to locate these bugs. 

 

We’ve explained what memory safety is and have established the importance of memory safe programming. Please join us next week for Part II of this blog. In which 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.  

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