> 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/tutorials/c-language.md).

# Investigate a C Application

You can use Ocular to investigate your C language applications. This tutorial illustrates how, by using the example of [CVE-2016-6480 Linux Kernel](https://nvd.nist.gov/vuln/detail/CVE-2016-6480). The vulnerability is a race condition that exists in the Linux Kernel version 4.7, in the `ioctl_send_fib in drivers/scsi/aacraid/commctrl.c` function.

This article will should you how to use Ocular to investigate your applications written in C. More specifically, you will see how Ocular finds the presence of [CVE-2016-6480 Linux Kernel](https://nvd.nist.gov/vuln/detail/CVE-2016-6480), a race condition in the **ioctl\_send\_fib** function. This function can be found in **drivers/scsi/aacraid/commctrl.c** in Linux kernels 4.7 (or earlier).

## Prerequisite <a href="#prerequisite" id="prerequisite"></a>

Before proceeding, please download the Linux kernel containing this vulnerability:

```
git clone https://github.com/torvalds/linux
cd linux
git checkout v4.7
```

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

## Step 1: Create the Code Property Graph (CPG) <a href="#step-1-create-the-code-property-graph-cpg" id="step-1-create-the-code-property-graph-cpg"></a>

Create a Code Property Graph (CPG) for the vulnerable driver:

```
/.shiftleft/ocular/fuzzyc2cpg.sh <path/to/kernel/linux/drivers/scsi/aacraid>
```

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

Once you have the CPG, start Ocular:

```
sl ocular
```

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

When prompted, load the CPG you generated:

```
importCpg("<path-to-cpg.bin.zip>")
```

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

Run `workspace` to ensure that your CPG has been loaded. If `loaded` is `true`, you're ready to proceed with your investigative queries.

## Examine the Code <a href="#examine-the-code" id="examine-the-code"></a>

Examine the interaction from user to kernel space with `copy_from_user`. To determine if any data from user space to kernel space is copied, use:

```
cpg.call.name("copy_from_user").code.p
```

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

Ocular returns the following:

```
copy_from_user(&qd, arg, sizeof (struct aac_query_disk))
copy_from_user(&dd, arg, sizeof (struct aac_delete_disk))
copy_from_user(&dd, arg, sizeof (struct aac_delete_disk))
copy_from_user((void *)kfib, arg, sizeof(struct aac_fibhdr))
copy_from_user(kfib, arg, size)
copy_from_user((void *)&f, arg, sizeof(struct fib_ioctl))
copy_from_user(&fibsize, &user_srb->count,sizeof(u32))
copy_from_user(user_srbcmd, user_srb,fibsize)
copy_from_user(p,sg_user[i],upsg->sg[i].count)
copy_from_user(p,sg_user[i],upsg->sg[i].count)
copy_from_user(p,sg_user[i],usg->sg[i].count)
copy_from_user(p, sg_user[i],\n\t\t\t\t\t\t\tupsg->sg[i].count)
```

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

This indicates that there doesn't seem to be any problems with data from user space to kernel space.

To look at flows from `copy_from_user`, use

```
def sinkArguments = cpg.method.name("copy_from_user").parameter.argument
println(sinkArguments.reachableBy(cpg.identifier).flows.p)
```

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

The query `println(sinkArguments.reachableByFlows(cpg.identifier).l.size)` returns the number 302.

Of interest is an estimate determining if the arguments of `copy_from_user` are sanitized. There are no direct definitions at `if` expressions, but information that flows into `if` expressions is available. To access this information using `Main.scala`, add the following lines

```
val reachingDefs1 = cpg.method
                       .name("copy_from_user")
                       .parameter
                       .argument
                       .reachableBy(cpg.identifier)
                       .toSet
```

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

`reachableByFlows` is used to construct and print out the flows. To filter all that detail, use `reachableBy` to have Ocular identify only the sources that are hit, rather than the details of the data flow paths. The following query collects the sources that are hit as a set

```
ocular> val reachingDefs2 = cpg.method
                       .name(".*less.*", ".*greater.*")
                       .parameter
                       .argument
                       .reachableBy(cpg.identifier)
                       .toSet
```

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

This query:

* Restricts the flows running to expressions that involve the less or greater keyword. Note that internally each binary operation (+,-,>,< etc.) is also treated as a function
* Tracks data dependency back to each identifier it hits and collected into a set

Now, check to see if there is an intersection between these two sets, which provides an estimate on which arguments of `copy_from_user` might be sanitized

```
reachingDefs1.intersect(reachingDefs2).foreach(elem => println(elem.code))
```

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

This query returns the following:

```
kmalloc(fibsize, GFP_KERNEL)
kmalloc(actual_fibsize - sizeof(struct aac_srb)\n\t\t\t  + sizeof(struct sgmap), GFP_KERNEL)
user_srbcmd->sg
kfib->header
size = le16_to_cpu(kfib->header.Size) + sizeof(struct aac_fibhdr)
actual_fibsize = sizeof(struct aac_srb) - sizeof(struct sgentry) +\n\t\t((user_srbcmd->sg.count & 0xff) * sizeof(struct sgentry))
* upsg = (struct user_sgmap64*)&user_srbcmd->sg
i = 0
user_srbcmd->sg
usg = kmalloc(actual_fibsize - sizeof(struct aac_srb)\n\t\t\t  + sizeof(struct sgmap), GFP_KERNEL)
user_srbcmd->sg
* upsg = &user_srbcmd->sg
user_srbcmd = kmalloc(fibsize, GFP_KERNEL)
i = 0
fibsize = 0
aac_fib_alloc(dev)
user_srbcmd->sg.count
kfib = fibptr->hw_fib_va
kfib->header.Size
actual_fibsize - sizeof(struct aac_srb)\n\t\t\t  + sizeof(struct sgmap)
fibptr = aac_fib_alloc(dev)
i = 0
fibptr->hw_fib_va
i = 0
```

![](/files/CRrxFX9wuboXxDrdB2dv)

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

The return shows that most potential checks involve some kind of a size element as expected.

Some outputs from `copy_from_user` have `kfib` as their first argument, which may be a pointer giving access to a header. The size of `kfib` seems to be involved with check `kfib->header.Size`. To confirm this in the source code (`commctrl.c`, line 90), use:

```
size = le16_to_cpu(kfib->header.Size) + sizeof(struct aac_fibhdr);
if (size < le16_to_cpu(kfib->header.SenderSize))
```

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

Use one of the following two queries to filter for `copy_from_user` looking for kfib as an argument:

```
cpg.call.name("copy_from_user").code(".*kfib.*").l
```

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

or

```
cpg.call.name("copy_from_user").filter(call => call.argument.code(".*kfib.*")).l
```

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

To print the return:

```
cpg.call.name("copy_from_user").filter(call => call.argument.code(".*kfib.*")).l.foreach(call => println(call.code))
```

![](/files/CRrxFX9wuboXxDrdB2dv)

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

You should see output as follows:

```
copy_from_user((void *)kfib, arg, sizeof(struct aac_fibhdr))
copy_from_user(kfib, arg, size)
```

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

Next, find the data flow from these sinks to a common ancestor which defines `kfib`. This helps ensure that there is no other definition of `kfib` which might have a double fetch.

```
val cfu1 = cpg.call.name("copy_from_user").code(".*kfib.*").l.head.start.reachableBy(cpg.identifier).toSet

val cfu2 = cpg.call.name("copy_from_user").code(".*kfib.*").l.last.start.reachableBy(cpg.identifier).toSet.intersect(cfu1)

cfu2.foreach(elem => println(elem.code + " " + elem.lineNumber.get)
```

![](/files/CRrxFX9wuboXxDrdB2dv)

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

This pattern is similar to reaching definitions to sanitizers. The start tells Ocular to start a fresh traversal at the given node. In this case, head and last are filtered to get those nodes. The output is as follows:

```
(aac_fib_alloc(dev),71)
(kfib = fibptr->hw_fib_va,76)
(fibptr = aac_fib_alloc(dev),71)
(fibptr->hw_fib_va,76)
```

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