CWE-413

Improper Resource Locking. [Resource-Locking-Problems, Improper-Control-Of-A-Resource-Through-Its-Lifetime, Insufficient-Control-Flow-Management]

Required inputs: IR

The product does not lock or does not correctly lock a resource when the product must have exclusive access to the resource. When a resource is not properly locked, an attacker could modify the resource while it is being operated on by the product. This might violate the product's assumption that the resource will not change, potentially leading to unexpected behaviors.
Demonstrative Examples
Example 1

The following function attempts to acquire a lock in order to perform operations on a shared resource.

Example Language:C
    void f(pthread_mutex_t *mutex) {
        pthread_mutex_lock(mutex);

        /* access shared resource */


        pthread_mutex_unlock(mutex);
    }

However, the code does not check the value returned by pthread_mutex_lock() for errors. If pthread_mutex_lock() cannot acquire the mutex for any reason, the function may introduce a race condition into the program and result in undefined behavior.

In order to avoid data races, correctly written programs must check the result of thread synchronization functions and appropriately handle all errors, either by attempting to recover from them or reporting them to higher levels.

Example Language:C
    int f(pthread_mutex_t *mutex) {
        int result;

        result = pthread_mutex_lock(mutex);
        if (0 != result)
            return result;


        /* access shared resource */


        return pthread_mutex_unlock(mutex);
    }
Example 2

This Java example shows a simple BankAccount class with deposit and withdraw methods.

Example Language:Java (Unsupported language for documentation only)
    public class BankAccount {
        // variable for bank account balance
        private double accountBalance;

        // constructor for BankAccount
        public BankAccount() {
            accountBalance = 0;
        }

        // method to deposit amount into BankAccount
        public void deposit(double depositAmount) {
            double newBalance = accountBalance + depositAmount;
            accountBalance = newBalance;
        }

        // method to withdraw amount from BankAccount
        public void withdraw(double withdrawAmount) {
            double newBalance = accountBalance - withdrawAmount;
            accountBalance = newBalance;
        }

        // other methods for accessing the BankAccount object
        ...
    }

However, the deposit and withdraw methods have shared access to the account balance private class variable. This can result in a race condition if multiple threads attempt to call the deposit and withdraw methods simultaneously where the account balance is modified by one thread before another thread has completed modifying the account balance. For example, if a thread attempts to withdraw funds using the withdraw method before another thread that is depositing funds using the deposit method completes the deposit then there may not be sufficient funds for the withdraw transaction.

To prevent multiple threads from having simultaneous access to the account balance variable the deposit and withdraw methods should be synchronized using the synchronized modifier.

Example Language:Java (Unsupported language for documentation only)
    public class BankAccount {
        ...
        // synchronized method to deposit amount into BankAccount
        public synchronized void deposit(double depositAmount) {
            ...
        }

        // synchronized method to withdraw amount from BankAccount
        public synchronized void withdraw(double withdrawAmount) {
            ...
        }

        ...
    }

An alternative solution is to use a lock object to ensure exclusive access to the bank account balance variable. As shown below, the deposit and withdraw methods use the lock object to set a lock to block access to the BankAccount object from other threads until the method has completed updating the bank account balance variable.

Example Language:Java (Unsupported language for documentation only)
    public class BankAccount {
        ...
        // lock object for thread access to methods
        private ReentrantLock balanceChangeLock;

        // condition object to temporarily release lock to other threads
        private Condition sufficientFundsCondition;

        // method to deposit amount into BankAccount
        public void deposit(double amount) {
            // set lock to block access to BankAccount from other threads
            balanceChangeLock.lock();
            try {
                double newBalance = balance + amount;
                balance = newBalance;

                // inform other threads that funds are available
                sufficientFundsCondition.signalAll();
            } catch (Exception e) {...}
            finally {
                // unlock lock object
                balanceChangeLock.unlock();
            }
        }

        // method to withdraw amount from bank account
        public void withdraw(double amount) {
            // set lock to block access to BankAccount from other threads
            balanceChangeLock.lock();
            try {
                while (balance < amount) {
                    // temporarily unblock access

                    // until sufficient funds are available
                    sufficientFundsCondition.await();
                }
                double newBalance = balance - amount;
                balance = newBalance;
            } catch (Exception e) {...}
            finally {
                // unlock lock object
                balanceChangeLock.unlock();
            }
        }
        ...
    }
Excerpts from CWE [https://cwe.mitre.org], Copyright (C) 2006-2026, the MITRE Corporation. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

discarded_return_with_entity

Return value of function discarded.

None

False

unhandled_return_value

Return value of function call not properly checked.

None

False

Options

allow_assignment_to_globals

allow_assignment_to_globals : bool = False

Whether assignment to global / static variables should be allowed. If set to false, an error will be reported if the returned value is assigned to a global variable and any call is performed before checking the return (i.e., some other routine could access the return value before checking it).
 

allow_assignment_to_variables_with_pointers

allow_assignment_to_variables_with_pointers : bool = True

Whether assignment to variables of which the address has been taken somewhere should be allowed. If set to false, an error will be reported if the return value is assigned to such a variable, to ensure that the return value is checked locally, before any access from outside is possible.
 

allowed_functions

allowed_functions : set[bauhaus.analysis.config.FunctionName] = {'memcpy', 'memmove', 'memset', 'strcat', 'strcpy', 'strncat', 'strncpy'}

Calls to these functions are ignored.
 

check_operators

check_operators : bool = False

Also check operator calls. Unused return values of assignment operators are only reported if given in function style syntax.
 

functions

functions

Type: dict[bauhaus.analysis.config.QualifiedName, bauhaus.ir.common.algorithms.matchers.Matcher]

Default:

{
   'pthread_mutex_lock': <bauhaus.rules.axivion.expressions.calls.unhandled_return_value.BinaryRelationAnyMatcher object at 0x7f6f1b83a0b0>,
   'pthread_mutex_trylock': <bauhaus.rules.axivion.expressions.calls.unhandled_return_value.BinaryRelationAnyMatcher object at 0x7f6f1b83a0b0>
}
Allows to declare function names for which a check must exist. The check is expressed as an IR pattern.
 

known_check_functions

known_check_functions : set[bauhaus.analysis.config.FunctionName] = set()

Collection of functions which are known to test return values of functions under test.
 

report_references

report_references : bool = False

Report returned references. For operators, unused returned references are only reported if given in function style syntax.