Gaining Bounds-checking on Trailing Arrays in the Upstream Linux Kernel
Emu (Nurin) | Wed 17 Apr 10:45 a.m.–11:30 a.m.
Presented by
-
Gustavo A. R. Silva
@embeddedgus
https://embeddedor.com/blog/
Gustavo works full-time as an upstream Linux kernel Engineer, focused on security. For the past several years, he’s been hunting and fixing bugs and issues all over the Linux kernel. He is an active contributor and member of the Kernel Self-Protection Project. His work in the kernel community is supported by The Linux Foundation and Google. Additionally, Gustavo is a regular speaker at Kernel Recipes.
Gustavo A. R. Silva
@embeddedgus
https://embeddedor.com/blog/
Abstract
Trailing arrays at the end of a structure are a common code construct in the Linux kernel. While they are often dynamically-sized, trailing arrays can also be fixed in size, defined at compile-time and remaining constant throughout their lifetime. It's a little-known fact that compilers like GCC (and Clang) have historically treated all trailing arrays as if they were flexible in size. This approach becomes problematic if we want to rely on the compiler to detect out-of-bounds issues on these arrays, both at compile-time and run-time.
To effectively address this, the compiler must first accurately differentiate between dynamically-sized and fixed-size trailing arrays. The introduction of the -fstrict-flex-arrays option in GCC 13 (and Clang 16) marks a significant step towards this goal. Concurrently, in Kernel Self-Protection Project, we have been converting trailing zero-length and one-element arrays --commonly known as fake flexible arrays-- into modern C99 flexible-array members for years.
We will see how the combination of these efforts, along with the implementation of some crucial compiler attributes, is paving the way towards eliminating out-of-bounds vulnerabilities on trailing arrays in the upstream Linux kernel. Furthermore, we will explore how this work is closely related to and contributes to the latest efforts in hardening key APIs like memcpy(), and in globally enabling options like -Warray-bounds.
Trailing arrays at the end of a structure are a common code construct in the Linux kernel. While they are often dynamically-sized, trailing arrays can also be fixed in size, defined at compile-time and remaining constant throughout their lifetime. It's a little-known fact that compilers like GCC (and Clang) have historically treated all trailing arrays as if they were flexible in size. This approach becomes problematic if we want to rely on the compiler to detect out-of-bounds issues on these arrays, both at compile-time and run-time. To effectively address this, the compiler must first accurately differentiate between dynamically-sized and fixed-size trailing arrays. The introduction of the -fstrict-flex-arrays option in GCC 13 (and Clang 16) marks a significant step towards this goal. Concurrently, in Kernel Self-Protection Project, we have been converting trailing zero-length and one-element arrays --commonly known as fake flexible arrays-- into modern C99 flexible-array members for years. We will see how the combination of these efforts, along with the implementation of some crucial compiler attributes, is paving the way towards eliminating out-of-bounds vulnerabilities on trailing arrays in the upstream Linux kernel. Furthermore, we will explore how this work is closely related to and contributes to the latest efforts in hardening key APIs like memcpy(), and in globally enabling options like -Warray-bounds.