SkillAgentSearch skills...

OpenOSC

Open Object Size Checking: Library to detect buffer overflows in C/C++ code

Install / Use

/learn @cisco/OpenOSC
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

OpenOSC Library - README

Table of Contents

Licensing

This project's licensing restrictions are documented in the file 'LICENSE' under the root directory of this release. Basically it's ASL 2.0 licensed.

Overview

OpenOSC is an open-source object size check library written in C. It has been developed in order to promote the use of compiler builtin object size check capability for enhanced security. It provides robust support for detecting buffer overflows in various functions that perform operations on memory and strings. Not all types of buffer overflows can be detected with this library, but it does provide an extra level of validation for some functions that are potentially a source of buffer overflow flaws. It protects both C and C++ code.

Design Considerations

The OpenOSC library is a standalone library with the following features:

  • Metrics to understand buffer overflow detection effectiveness
  • Ability to detect source buffer overread as well as buffer overflow
  • Truncate overflow past the end of buffer in running process
  • Abort behavior configurable
  • Extensible to additional data movement routines
  • Extension for SafeC bounds checking library available

OpenOSC contains two parts: the mapping header files and the runtime library.

The runtime library libopenosc.so contains the code to do runtime buffer overflow checks. This library is required to be linked by all packages.

The mapping header files contain the code to perform compile-time buffer overflow checks and map various memory routines to the runtime check routines in the runtime library.

The mapping header files are only required in the package build environment, while the runtime library is required in both the deployment environment and the package build environment.

Two OSC function mapping methods are supported in OpenOSC:

#define OPENOSC_ASM_LABEL_REDIRECT_METHOD    1
#define OPENOSC_FUNC_MACRO_REDEFINE_METHOD   2

They are two different mapping methods. The ASM_LABEL_REDIRECT method uses __asm__ label to redirect an alias function to the real function at assembly language level, while keeping them as different functions at C language level. The FUNC_MACRO_REDEFINE method uses macro redefining mechanism to directly redefine a foo function as a new openosc_foo function at C language level.

The two methods both have advantages and disadvantages.

Note that if your compiler is clang, the ASM_LABEL_REDIRECT method requires that your clang version must be >= 5.0 version.

The default mapping method is 1, the ASM_LABEL_REDIRECT method. User can choose the FUNC_MACRO_REDEFINE method by adding the below define to CFLAGS:

CFLAGS += "-DOPENOSC_MM=2"

New mapping method can be added as needed in future.

  • Built-in OSC-METRICS feature

The built-in OSC-METRICS feature can be enabled by the below flag to CFLAGS:

CFLAGS += "-DOPENOSC_METRIC_FEATURE_ENABLED"

Additionally, the objsize-METRICS feature can be enabled by the below:

CFLAGS += "-DOPENOSC_METRIC_OBJSIZE_ENABLED"

Two OSC-watermarking methods are supported in OpenOSC:

#define RTD_ASM_BYTE_METHOD 1
#define RTD_ASM_LOC_METHOD 2

The default method is RTD_ASM_LOC_METHOD. This method uses DWARF debugging info, so it requires the -g CFLAGS to take effect. And the OSC-METRICS info can be stripped via the strip command, just like other debugging info. The other method RTD_ASM_BYTE_METHOD directly inserts MAGIC words into the code, so it occupies code memory, and does not require -g CFLAGS to take effect, also OSC-METRICS info cannot be stripped. You can add "-DOPENOSC_METRIC_METHOD=1" to CFLAGS to force using the RTD_ASM_BYTE_METHOD.

After building your binaries with the above CFLAGS, You can run the oscmetrics tool to collect the OSC-METRICS:

$ tools/oscmetrics.py -bmwv -d your-dir > metrics-report.txt

If the non-default RTD_ASM_BYTE_METHOD is used to generate OSC-METRICs info, then you need to add the -t option to specify the type:

$ tools/oscmetrics.py -bmwv -d your-dir -t byte_inline > metrics-report.txt

It can generate a WatermarkPC decoding table as below:

###################################################
OSC-METRIC WatermarkPC summary for all binary files
## Note: DSTSIZE is format of: (dstsize, srcsize, copylen), (dstsize, copylen), or (dstsize).
##       Copy-length of -9 means that the copy-length parameter
##       for memcpy is unknown (non-constant) at compile-time.
###################################################

FileIndex: 3 ./test/openosc_test_cpp
Idx   PC           OSCFUNC     CASE#  DSTSIZE     SRCFUNC                       SRCLINE
---------------------------------------------------------------------------------------
0     0x400ce5     memcpy      CASE7  5,20,4      openosc_test_memcpy()         openosc_test_cpp.cpp:23
1     0x400de4     memcpy      CASE2  5,20,7      openosc_test_memcpy()         openosc_test_cpp.cpp:28
2     0x400e31     memcpy      CASE2  5,20,8      openosc_test_memcpy()         openosc_test_cpp.cpp:30
3     0x400ea3     memcpy      CASE3  5,20,-9     openosc_test_memcpy()         openosc_test_cpp.cpp:33
4     0x400f15     memcpy      CASE3  5,20,-9     openosc_test_memcpy()         openosc_test_cpp.cpp:34
5     0x400f8b     memcpy      CASE3  5,20,-9     openosc_test_memcpy()         openosc_test_cpp.cpp:36
6     0x400fdd     memcpy      CASE2  0,20,1      openosc_test_memcpy()         openosc_test_cpp.cpp:38
7     0x401049     memcpy      CASEa  -1,20,2     openosc_test_memcpy()         openosc_test_cpp.cpp:40

One trick: To generate the binary even when buffer overflow errors exist, add -DOPENOSC_OVERFLOW_ERROR_OUT_DISABLE to CFLAGS. This will just print warnings instead of errors at compile-time so that binary can be generated. If you use -DOPENOSC_MM=2 CFLAGS, then you can add -DOPENOSC_METRIC_ONLY_MODE to CFLAGS to collect OSC metrics only.

Trick #2: the oscmetrics.py tool ignores intermediate relocatable *.o files by default. You can add --scan_all_elf_files option to force the tool to scan the *.o files too.

Trick #3: for cross-compilation binaries, you may need to specify the location of cross-compilation tools: objdump, addr2line, etc. in a configfile, then run oscmetrics.py with -c configfile option to get correct results.

Relationship to FORTIFY_SOURCE

Most compilers (gcc/clang/icc) provide the FORTIFY_SOURCE feature. It is enabled by adding -D_FORTIFY_SOURCE=2 option to CFLAGS.

CFLAGS += "-D_FORTIFY_SOURCE=2"

OpenOSC and compiler's FORTIFY_SOURCE are both built upon the compiler's builtin object size check capability, thus are equivalent in providing memory overflow detection/protection. As a result, a package can only be compiled with one of them: either OpenOSC or FORTIFY_SOURCE, but not both. FORTIFY_SOURCE and OpenOSC are mutually exclusive. OpenOSC will automatically detect the existence of _FORTIFY_SOURCE flag, and disable OpenOSC if detected.

OpenOSC can co-exist with FORTIFY_SOURCE. That is, some packages or binaries can be compiled with OpenOSC, and some other packages/binaries can be compiled with FORTIFY_SOURCE. They can co-exist on the same Linux system, and will be protected by different runtime libraries.

Here is a comparison summary table:

| Feature/Behavior | FORTIFY_SOURCE | OpenOSC | | ----------------- | -------------- | ------------------- | | OSC Metrics | No support | Supported | | Source Overread | No support | Supported | | Traceback | No traceback | Print traceback | | Abort behavior | Always abort | Configurable | | Copy truncation | No truncate | Yes/Configurable | | Logging | No syslog | Syslog/Configurable | | Cover new routine | Not easy | Easy |

Metric-only Mode for FORTIFY_SOURCE

The OpenOSC OSC-METRICS feature has been enhanced to work with FORTIFY_SOURCE feature enabled.

It is enabled by adding -DOPENOSC_METRIC_ONLY_MODE option to CFLAGS.

CFLAGS += "-include openosc.h -DOPENOSC_METRIC_ONLY_MODE -D_FORTIFY_SOURCE=2"

This does not turn on OSC-METRICS for objsize/copylen. If you want it, add the OPENOSC_METRIC_OBJSIZE_ENABLED flag.

CFLAGS += "-include openosc.h -DOPENOSC_METRIC_ONLY_MODE -DOPENOSC_METRIC_OBJSIZE_ENABLED -D_FORTIFY_SOURCE=2"

This will generate the same OSC-METRICS watermarks for the compiled binary file with the OPENOSC_METRIC_FEATURE_ENABLED flag. And you can use the same tools/oscmetrics.py script to decode the OSC-METRICS watermarks.

With this OPENOSC_METRIC_ONLY_MODE flag, the memory/string functions will always be mapped to itself, except for the addition of OSC-METRICS. The generated assembly/binary code should also be the same as before, except for the addition of OSC-METRICS. Of course, out-of-order generation of some assembly instructions are expected, and still considered the same code.

The nice thing about this metric-only mode is that you don't need to build the libopenosc.so library. You don't need to change your linking LDFLAGS, since you don't link with any additional library. The only change required is CFLAGS.

Note this Metric-only mode also works without FORTIFY_SOURCE feature. It will map all functions to themselves, except for the addition of OS

Related Skills

View on GitHub
GitHub Stars32
CategoryDevelopment
Updated1mo ago
Forks10

Languages

C

Security Score

90/100

Audited on Feb 18, 2026

No findings