Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • Guided learning
      Receive custom learning paths powered by our AI assistant.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

Improve basic programming safety with Rust lang

May 21, 2024
Sandipan Roy
Related topics:
CompilersRustSecure coding
Related products:
Red Hat Enterprise Linux

    As software becomes increasingly integrated into our daily lives, the importance of secure coding and programming safety cannot be overstated. From online banking to healthcare records, sensitive information is being transmitted and stored in software systems, making them prime targets for cyber attacks. As a result, it's crucial for developers to understand and implement best practices for secure coding to ensure the safety and privacy of user data. In this article, we explore the various techniques and strategies that can be used to make software more secure and resilient to attacks.

    Programming safety

    Programming safety is multidimensional and subjective. We consider four types of programming safety: memory safety, null safety, type safety, and thread safety.

    Memory safety

    A service provided by the standard library and the runtime where memory access violations are impeded is known as memory safety. Memory safety is a property of a programming language or system that ensures programs only access memory locations that they are allowed to access. Essentially, memory safety prevents certain types of errors and vulnerabilities related to memory access, such as buffer overflows, dangling pointers, and use-after-free errors. Some languages like C and C++ are not secure when best coding practices are not followed, leaving them insecure by nature. Double-free errors in C or C++ occur when you accidentally free a pointer twice. A use-after-free (UAF) error or dangling pointer error occurs when software continues to utilize a pointer after it has been released. These actions are examples of undefined behavior since they are unexpected and create security holes rather than solely crashing the application. A software crash is advantageous in some situations since it won't lead to a security risk.

    Problems with memory safety cause most of the Common Vulnerabilities and Exposures (CVEs) that we come across. A hacker might take advantage of a memory vulnerability to take over a system, execute malicious code, or expose sensitive information. A memory-safe language will crash, panic, or fail if you attempt to access an out-of-bounds array element; this is expected behavior. This is why CVEs and urgent fixes are frequently issued in response to memory-related problems in C and C++ systems. Other memory-unsafe activities in C and C++ include using memory that has been deallocated, invalidating iterators, and more. Even languages that are memory-safe with weaker security features guard against these security flaws.

    See the use-after-free example below, where the pointer in C is instantly released when an error has occurred. The LogError method later uses this reference improperly. The code is checking if the err is true; if it is true, it sets the variable Automatic Bug Reporting Tool (ABRT) to 1 and frees the memory allocated to the pointer ptr. 

    // Allocate memory of size SIZE and assign the pointer to "ptr"
    char* ptr = (char*)malloc (SIZE);
    // If an error occurs
    if (err) {
        // Set the "abrt" variable to 1
        abrt = 1;
        // Free the memory allocated to "ptr"
        free(ptr);
    }
    ...
    // If "abrt" is set to 1
    if (abrt) {
        // Log an error message with the given message and the value of "ptr"
        logError("Operation Aborted Before Commit", ptr);
    }
    

    Null safety

    Memory safety and null safety are connected. Nothing is required in garbage collecting languages for pointers to be released when they are not in use, which causes problems such as null pointer exceptions. Most memory-safe programming languages permit the use of null as a value, which also results in null pointer problems.

    See the null pointer dereference example below using Java, where the programmer takes for granted that the system's cmd attribute is always defined. When attempting to use the trim() function, the application raises a null pointer exception if an attacker is able to manipulate the environment of the program such that cmd is not specified.

    // Get the value of the "cmd" system property and assign it to a variable named "cmd"
    String cmd = System.getProperty("cmd");
    // Remove any leading and trailing whitespaces from the "cmd" variable
    cmd = cmd.trim();
    

     Type safety

    We access a variable in a type-safe language exactly as the data is stored. As a result, we are confident in dealing with this data without explicitly verifying the data type at runtime. Lacking type safety can cause security problems because it is crucial for assuring memory safety.

    Languages that are not type-safe are susceptible to low-level attacks because an attacker can modify the data type and manipulate the data structure to acquire private information. Although this kind of attack is uncommon, it does happen occasionally.

    In C++, the code below will compile without error but the result of the addition will be incorrect because x and y have different types, and the addition operation will be done between them but the result will be wrong.

    int x = 5;
    double y = x; // implicit type conversion
    ​​​​​​​int sum = x + y; // this will compile, but the result will be incorrect

    Thread safety

    When using a language that is thread-safe, you may have many threads access the same memory at the same time without having to worry about data races. Mutual exclusion locks (mutexes), thread synchronization, and message forwarding mechanisms are typically used to accomplish this. Thread safety may also result in security problems for developers.

    Two types of vulnerabilities may result from thread safety problems. The first is when one thread overwrites data from another, and second is the interlacing of data from many threads. The most common example of thread safety is a time-of-check time-of-use (TOCTOU) attack.

    In the code below we see a TOCTOU attack, where we consider that many threads will call this code block. Depending on when RedHat.txt was last edited, the switch statement will run a different piece of code. If RedHat.txt changed while this code block was running on different threads, it would be possible for this code block to crash, and our switch case may produce unexpected outcomes.

    #include <sys/types.h>
    #include <sys/stat.h>
    ...
    // Declare a struct stat variable named sb
    struct stat sb;
    // Get information about the file "RedHat.txt" and store it in the sb variable
    stat("RedHat.txt",&sb);
    // Print the change time of the file
    printf("file change time: %d\n",sb.st_ctime);
    // Use the modulo operator to get the remainder of dividing the file change time by 2
    switch(sb.st_ctime % 2){
        // If the remainder is 0
        case 0: 
            printf("Option 1\n"); // Print "Option 1"
            break; 
        // If the remainder is 1
        case 1:
            printf("Option 2\n"); // Print "Option 2"
            break; 
        // If none of the above cases are true, (which should not happen)
        default: 
            printf("This should be unreachable?\n"); 
            break;
    }
    

    Why Rust?

    Let's discuss Rust and why it could be a better alternative when it comes to secure coding. Memory safety or safety in general is one of the important features of the Rust language. By default, the compiler will fail to compile insecure code. Rust provides an unsafe keyword that allows us to bypass the security guarantees provided by the Rust compiler, however, its usage won’t be required in most cases. Rust uses an inventive ownership structure and a borrow checker integrated into the compiler to guarantee memory safety at build time. Through the use of extra runtime checks and static compile-time analysis, Rust ensures memory safety while removing many types of memory issues. At the language level, null does not exist. Instead, the Option enum in Rust is available and may be used to indicate whether a value is present or not. This means that null pointer exceptions will not occur with Rust since the resulting code is much simpler and null-safe.

    Rust is one of the most memory-efficient languages because of the ownership and borrowing systems, which helps programmers avoid problems with manual memory management and garbage collection. It is as fast and efficient with memory as C and C++, and it also has higher memory safety than garbage-collected programming languages such as Java and Go.

    // Create a new string "Hello" and assign it to a variable named "s1"
    let s1 = String::from("Hello");
    // Assign the value of "s1" to a new variable named "s2"
    let s2 = s1;
    // Try to print the value of "s1"
    println!("{}", s1); // Does not compile
    

    The code above creates a new string "Hello" using the String::from function and assigns it to a variable s1. Then, it creates a new variable s2 and assigns s1 to it. The println!("{}", s1); statement is trying to print the value of s1, but it does not compile because after the assignment s2 = s1, the value of s1 has been moved to s2. 

    In Rust, variables have a property called ownership, and when a value is assigned to another variable, the original variable loses its ownership of the value. In this case, after the assignment s2 = s1, s1 no longer owns the string "Hello", so it cannot be used or accessed.

    It is important to note that this is a feature in Rust that helps prevent data races and other undefined behavior by ensuring that there is only one owner of a value at any given time.

    // Create a mutable variable named "num" with the value of 5
    let mut num = 5;
    // Create a constant pointer "r1" to the value of "num"
    let r1 = &num as *const i32;
    // Create a mutable pointer "r2" to the value of "num"
    let r2 = &mut num as *mut i32;
    // Enter an unsafe block
    unsafe {
        // Dereference pointers "r1" and "r2" and print their values
        println!("{}{}", *r1, *r2); 
        // Dereferencing a raw pointer is only allowed within unsafe block
    }
    

    The code above creates a mutable variable num with the value of 5 and then creates two pointers: r1 and r2. r1 is a const pointer to the value of num and is created using the & operator, which creates a reference to the value of num, and the as *const i32 syntax, which casts the reference to a raw pointer. r2 is a mutable pointer to the value of num and is created using the &mut operator, which creates a mutable reference to the value of num, and the as *mut i32 syntax, which casts the reference to a raw pointer. The unsafe keyword is used to indicate that the code inside the block may be dangerous and could cause undefined behavior if it’s not used correctly. The println!("{}{}", *r1, *r2); statement is trying to dereference the pointers r1 and r2, and print the values they point to. Dereferencing a raw pointer means accessing the value it points to. This can only be done inside an unsafe block because it could cause undefined behavior if the pointer is not valid or if it is a null pointer.

    It's important to note that using raw pointers in Rust is generally discouraged, and it is recommended to use references or smart pointers instead because they provide safer and more robust ways of managing memory.

    Type safety in Rust

    Because Rust is statically typed, type safety is ensured by rigorous compile-time type verification. Using the dyn keyword, Rust permits a degree of dynamic typing. Although this is the case, type safety is guaranteed by the compiler and sophisticated type inference. 

    let x: i32 = 5;
    let y: f64 = x as f64; // explicit type conversion
    let sum = x + y; // this will not compile because x and y have different types

    In the example shown above, variable x is of type i32, and variable y is of type f64. When we try to add these two variables together, the Rust compiler will raise an error because these two variables have different types. The addition operation between them is not allowed and the compiler will not allow the code to be compiled. This is an example of how Rust's type safety helps prevent logical errors and makes the code more robust.

    Thread safety in Rust

    Rust offers common library functionality including channels, mutex, Automatic Reference Counted (ARC) pointers, as well as a guarantee for thread safety using notions similar to those used for memory safety. Secure Rust allows for limitless read-only references to a resource as well as one mutable reference at any given moment. Due to the ownership mechanism, a shared state cannot accidentally start a data race. This gives us the peace of mind to concentrate on the code and let the compiler handle shared data across threads.

    Outside programming safety, Rust also has features that enhance system security. A few features include:

    • Awesome concurrency support
    • Flexible pattern matching
    • Immutable by default
    • Easy to write functional code
    • Easy to write system-level programming
    • Growing community
    • Helpful compiler messages
    • Amazing tooling support (Cargo, crates.io, rust-analyzer, etc.)

    Rust at Red Hat

    Rust has a growing community and is actively being used by large corporations, including Red Hat projects like the stratis storage project utilizes Rust heavily also Podman recently released a new network stack written in Rust called Netavark and Aardvark, in addition to the existing CNI stack. Other notable projects written in Rust are rpm-ostree, coreos-installer, and aya eBPF library for Rust. And some well known upstream Projects like Linux Kernel, Firefox, Thunderbird, python-cryptography are actively using Rust.

    Conclusion

    Programming safety and secure coding are critical aspects of software development, particularly in the field of systems programming. With a focus on memory safety and ownership, Rust provides a number of features that help developers write more secure code. The ownership model, lifetime annotations, and borrow checker work together to prevent common programming errors such as null or dangling pointer references. Additionally, their type system and strong type inference make it more difficult for developers to introduce type-related bugs into their code. Rust’s community also provides various tooling to help developers detect and prevent potential vulnerabilities like memory leaks and data races. Overall, Rust's focus on safety and security makes it an excellent choice for systems programming and other critical applications.

    References

    • Learn Rust - Rust Programming Language
    • Introduction - Secure Rust Guidelines
    • RustSec
    • GitHub - rust-secure-code/projects: Contains a list of security related Rust projects.
    • Black Hat Rust
    • Fearless Concurrency with Rust
    • Concurrency - The Rust Programming Language
    • Understanding Rust Thread Safety
    • RustZone: Writing Trusted Applications in Rust
    • The art and science of secure open source software development
    • Secure Coding Tutorials | Red Hat Developer

    Related Posts

    • Support lifecycle for Clang/LLVM, Go, and Rust in Red Hat Enterprise Linux 8

    • How to create Python binding for a Rust library

    • Use multiple compilers to build better projects

    • How to create C binding for a Rust library

    • Adding keystores and truststores to microservices in Red Hat OpenShift

    • eBPF application development: Beyond the basics

    Recent Posts

    • Every layer counts: Defense in depth for AI agents with Red Hat AI

    • Fun in the RUN instruction: Why container builds with distroless images can surprise you

    • Trusted software factory: Building trust in the agentic AI era

    • Build a zero trust AI pipeline with OpenShift and RHEL CVMs

    • Red Hat Hardened Images: Top 5 benefits for software developers

    What’s up next?

    With growing threats and attack vectors, implementing Zero Trust across your organization is an imperative. Read Build a foundation of security with Zero Trust and automation to find out more about the Zero Trust model and how to automate it.

    Get the e-book
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Chat Support

    Please log in with your Red Hat account to access chat support.