> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/sast-and-sca/ocular/sample-use-cases/tracking-non-atomic.md).

# How to Track Non Atomic Data Types

In general, processes do not execute atomically, since the operating system may interrupt processes between essentially any two instructions, allowing other processes to run. If your application's process is not prepared for these interruptions, another process may be able to interfere with it, causing the data structures to end up in inconsistent states if arbitrary code is executed between them.

A non atomic condition is a vulnerability to interference caused by untrusted processes. These are conditions caused by processes running other, different programs, which introduce other actions between steps of the program. These other programs might be invoked by an attacker.

This use case illustrates how to use Ocular to analze for non atomic data types, using the FreeRTOS real-time operating system kernel as the example target application.

## Downloading the FreeRTOS Application <a href="#downloading-the-freertos-application" id="downloading-the-freertos-application"></a>

[Download the FreeRTOS application](https://www.freertos.org/a00104.html) and unzip the source code into the Ocular `subjects` directory, for example `subjects/FreeRTOS`.

## Creating the FreeRTOS Application CPG <a href="#creating-the-freertos-application-cpg" id="creating-the-freertos-application-cpg"></a>

```
importCpg("subjects/FreeRTOS")
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

FreeRTOS's CPG is automatically loaded into memory and your workspace.

## Declaring an Array of Primitive Types <a href="#declaring-an-array-of-primitive-types" id="declaring-an-array-of-primitive-types"></a>

```
val primitiveTypes = List("int", "float", "double", "void", "size_t", "ANY", "void", "char", "short")
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Calling a Convenience Function to get LineNumber <a href="#calling-a-convenience-function-to-get-linenumber" id="calling-a-convenience-function-to-get-linenumber"></a>

```
def getLineNumber(ln : Option[Integer]) = (ln match { case Some(x) => x ; case None => 0 }).asInstanceOf[Int]
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Declaring a Data Structure of User Defined Types <a href="#declaring-a-data-structure-of-user-defined-types" id="declaring-a-data-structure-of-user-defined-types"></a>

```
case class UDT(uType : String, uName : String,  methodName : String, fileName : String, lineNumber : Integer)
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Acquiring all Identifiers into UDT Data Structure <a href="#acquiring-all-identifiers-into-udt-data-structure" id="acquiring-all-identifiers-into-udt-data-structure"></a>

Negative filter on primitive types.

```
val udtList = cpg.identifier.l.map {
   i => UDT(i.typeFullName, i.name, i.start.method.name.l.head, i.start.file.name.l.head, getLineNumber(i.lineNumber))
} filter {
   p => !primitiveTypes.exists(e => p.uType.contains(e))
} distinct
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Storing Findings in a Multimap Keyed by Identified methodName <a href="#storing-findings-in-a-multimap-keyed-by-identified-methodname" id="storing-findings-in-a-multimap-keyed-by-identified-methodname"></a>

```
import collection.mutable._
val udtMap = new HashMap[String,Set[UDT]] with MultiMap[String,UDT]
udtList.foreach { 
item => udtMap.addBinding(item.methodName, item)
}
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Storing Findings in a Multimap Keyed by Identified Type <a href="#storing-findings-in-a-multimap-keyed-by-identified-type" id="storing-findings-in-a-multimap-keyed-by-identified-type"></a>

```
import collection.mutable._
val udtMapByType = new HashMap[String,Set[UDT]] with MultiMap[String,UDT]
udtList.foreach { 
item => udtMapByType.addBinding(item.uType, item)
}

implicit def flat[K,V](kv: (K, Option[V])) = kv._2.map(kv._1 -> _).toList
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Calling a Convenience Function to get Range Given Start and End <a href="#calling-a-convenience-function-to-get-range-given-start-and-end" id="calling-a-convenience-function-to-get-range-given-start-and-end"></a>

```
def getRange(start : Int , end : Int) = start to end toList
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Giving functionName Return all callOuts within Scope of the Function <a href="#giving-functionname-return-all-callouts-within-scope-of-the-function" id="giving-functionname-return-all-callouts-within-scope-of-the-function"></a>

```
def getCallOutDetails(fnName:String) = cpg.method.name(fnName).callOut.l.map(co => (co.name , getLineNumber(co.lineNumber))).sortBy(_._2)
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Optimizing Name Replacement <a href="#optimizing-name-replacement" id="optimizing-name-replacement"></a>

```
def getCallOutDetails(fnName:String) = cpg.method.name(fnName).callOut.l.par.map(co => (co.name , getLineNumber(co.lineNumber))).toList.sortBy(_._2)
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Calling a Convenience Function for Holding Functions with CRITICAL SECTIONS with Ranges <a href="#calling-a-convenience-function-for-holding-functions-with-critical-sections-with-ranges" id="calling-a-convenience-function-for-holding-functions-with-critical-sections-with-ranges"></a>

```
case class WithCriticalSection(fnName : String, fileName : String, csRange : List[(String, Int, Int)])
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Specifying Functions to Optimize Ranging <a href="#specifying-functions-to-optimize-ranging" id="specifying-functions-to-optimize-ranging"></a>

For situations in which multiple critical sections exists in a method (function).

```
def getBounds(dataTuples : List[(String,Int)]) =
  dataTuples.foldLeft((List.empty[(String, Int)], Option.empty[(String, Int)])) {
    case ((state, previousSignal), signal) =>
      if (previousSignal.exists(_._1.contains("EXIT")) && signal._1.contains("EXIT")) {
        (state.dropRight(1) :+ signal, Some(signal))
      } else {
        (state :+ signal, Some(signal))
      }
  }

def getRange(enterExits : (List[(String, Int)], Option[(String, Int)])) =
  enterExits._1.foldLeft(
      (List.empty[(String, Int, Int)], Option.empty[(String, Int, Int)])) {
      case ((state, accSignal), signal) =>
        if (signal._1.contains("ENTER")) {
          (state, Some(("CRITICAL_SECTION_RANGE", signal._2, 0)))
        } else {
          val enterExt = accSignal.map(elem => elem.copy(_3 = signal._2))
          (state :+ enterExt.get, Option.empty)
        }
    }._1
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Getting Entire callOut Trace for Each Method that Encompasses taskENTER\_CRITICAL <a href="#getting-entire-callout-trace-for-each-method-that-encompasses-taskenter_critical" id="getting-entire-callout-trace-for-each-method-that-encompasses-taskenter_critical"></a>

```
val callMap = cpg.method.filter(_.callOut.name("taskENTER_CRITICAL")).l map {
  s => Map(s.name -> (s.start.file.name.head, getCallOutDetails(s.name.replaceAll("\\*",""))))
} reduce(_ ++ _)
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Getting Entire callOut Trace for Each Method that DOES NOT Encompass taskENTER\_CRITICAL <a href="#getting-entire-callout-trace-for-each-method-that-does-not-encompass-taskenter_critical" id="getting-entire-callout-trace-for-each-method-that-does-not-encompass-taskenter_critical"></a>

```
val callMapWithoutCS = cpg.method.filterNot(_.callOut.name("taskENTER_CRITICAL")).l map {
          s => Map(s.name -> (s.start.file.name.headOption.getOrElse("NOT_IDENTIFIED"), getCallOutDetails(s.name.replaceAll("\\*",""))))
        } reduce(_ ++ _)
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Optimizing Replacement <a href="#optimizing-replacement" id="optimizing-replacement"></a>

```
def timeTaken[R](block: => R): R = {
    val t0 = System.nanoTime()
    val result = block    // call-by-name
    val t1 = System.nanoTime()
    println("Elapsed time: " + (t1 - t0) + "ns")
    result
}

val mWithoutCS = cpg.method.filterNot(_.callOut.name("taskENTER_CRITICAL")).l

timeTaken {
   val callMapWithoutCS = mWithoutCS.map {
         s => Map(s.name -> (s.start.file.name.headOption.getOrElse("NOT_IDENTIFIED"),  getCallOutDetails(s.name.replaceAll("\\*",""))))
   } reduce(_ ++ _)
}
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Filtering callMap and Fitting into WithCriticalSection <a href="#filtering-callmap-and-fitting-into-withcriticalsection" id="filtering-callmap-and-fitting-into-withcriticalsection"></a>

```
val callMapFiltered = callMap map {
    case(k,v) => k -> (v._1 , getRange(getBounds(v._2.filter(t => t._1.contains("taskENTER_CRITICAL") || t._1.contains("taskEXIT_CRITICAL")))))
} map { case(k,v) => k -> WithCriticalSection(k,v._1,v._2) }
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Navigating using an Identifier <a href="#navigating-using-an-identifier" id="navigating-using-an-identifier"></a>

For example called `xQueueAddToSet`. Pick any type from `udtMapByType` say for instance tfp\_format.

```
…
"SIZEOF_LONG_LONG" -> Set(
    UDT("SIZEOF_LONG_LONG", "lng", "tfp_format", "../../Downloads/dahling-fw/Src/utils/tinystdio.c", 398),
    UDT("SIZEOF_LONG_LONG", "lng", "tfp_format", "../../Downloads/dahling-fw/Src/utils/tinystdio.c", 412),
    UDT("SIZEOF_LONG_LONG", "lng", "tfp_format", "../../Downloads/dahling-fw/Src/utils/tinystdio.c", 408)
  
...

val nonAtomicUsedInCS =callMap.getOrElse("tfp_format","NOT_FOUND")
val nonAtomicUsedInNoCS =callMapWithoutCS.getOrElse("tfp_format","NOT_FOUND")
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

If `(nonAtomicUsedInCS.size > 0 && nonAtomicUsedInNoCS.size > 0)`, this implies that a non atomic data type is used both in guarded context and not in guarded context, possibly leading to deadlock or starvation.

## Determining the Location Details of Where the Non Atomic Data type is Used <a href="#determining-the-location-details-of-where-the-non-atomic-data-type-is-used" id="determining-the-location-details-of-where-the-non-atomic-data-type-is-used"></a>

```
val udtSet = udtMap("xStreamBufferReset")
val callSiteData = callMapFiltered.get("xStreamBufferReset").get

udtSet.foreach {
  udt => callSiteData.csRange.map { item => 
    if((udt.lineNumber > item._2) && (udt.lineNumber < item._3)) {
      printf("[%s] is bound in critical section at [%d] between [%d] and [%d] in methodName [%s] located at [%s]\n", udt.uName, udt.lineNumber, item._2, item._3, udt.methodName, udt.fileName)
    }
  }
}
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)
