Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Improve basic programming safety with Rust lang

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

Share:

    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

    • Meet the Red Hat Node.js team at PowerUP 2025

    • How to use pipelines for AI/ML automation at the edge

    • What's new in network observability 1.8

    • LLM Compressor: Optimize LLMs for low-latency deployments

    • How to set up NVIDIA NIM on Red Hat OpenShift AI

    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

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    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

    Red Hat legal and privacy links

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

    Report a website issue