[libc-commits] [libc] [libc] Make struct ifreq compatible with linux/if.h (PR #225793)

Pavel Labath via libc-commits libc-commits at lists.llvm.org
Thu Sep 24 06:19:09 PDT 2026


https://github.com/labath updated https://github.com/llvm/llvm-project/pull/225793

>From 905ad6a696635154f7f643f5c7741d53cdc2de5f Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Wed, 23 Sep 2026 13:52:30 +0000
Subject: [PATCH 1/3] [libc] Make struct ifreq compatible with linux/if.h

When <linux/if.h> is included after <net/if.h>, compilation fails for
two reasons:
- <linux/libc-compat.h> expects libc to coordinate using the
  __UAPI_DEF_IF_IFREQ macro. Because we didn't define it, the kernel
  headers fell back to defining it as 1, which caused <linux/if.h> to
  redefine struct ifreq.
- <linux/if.h> defines field access macros (ifr_name, ifr_addr, etc.)
  pointing to members of named ifr_ifrn and ifr_ifru unions. Our struct
  ifreq used an anonymous union, so those defines broke member access.

To fix this:
- define __UAPI_DEF_IF_IFREQ 0 to prevent the kernel header from
  defining the struct
- modify `struct ifreq` to use named ifr_ifrn and ifr_ifru unions and
  defining matching member macros. This is necessary because kernel
  headers define the member macros even in the __UAPI_DEF_IF_IFREQ==0
  case.
- add an include test in test/include/net/linux to verify that including
  <linux/if.h> after <net/if.h> works as expected

While in there, I also move the struct definition into a linux
subfolder, as the type is linux-specific.

It's worth noting that including the headers in the opposite direction
is still broken, but that is also true for all other libc
implementations.

Assisted-by: Gemini
---
 libc/include/llvm-libc-types/CMakeLists.txt   | 11 +++-
 .../llvm-libc-types/linux/CMakeLists.txt      |  9 +++
 .../llvm-libc-types/linux/struct_ifreq.h      | 59 +++++++++++++++++++
 libc/include/llvm-libc-types/struct_ifreq.h   | 25 +-------
 libc/test/include/CMakeLists.txt              |  2 +
 libc/test/include/net/CMakeLists.txt          |  4 ++
 libc/test/include/net/linux/CMakeLists.txt    | 10 ++++
 .../net/linux/net_if_and_linux_if_test.cpp    | 27 +++++++++
 8 files changed, 122 insertions(+), 25 deletions(-)
 create mode 100644 libc/include/llvm-libc-types/linux/struct_ifreq.h
 create mode 100644 libc/test/include/net/CMakeLists.txt
 create mode 100644 libc/test/include/net/linux/CMakeLists.txt
 create mode 100644 libc/test/include/net/linux/net_if_and_linux_if_test.cpp

diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index ea710ef48857cf..c0db5ab81f382f 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -30,6 +30,9 @@ function(add_type_header name)
   )
 endfunction()
 
+add_header(sa_family_t HDR sa_family_t.h)
+add_header(struct_sockaddr HDR struct_sockaddr.h DEPENDS .sa_family_t)
+
 if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
   add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
 endif()
@@ -263,9 +266,7 @@ add_header(
 )
 add_header(wint_t HDR wint_t.h)
 add_header(wctype_t HDR wctype_t.h)
-add_header(sa_family_t HDR sa_family_t.h)
 add_header(socklen_t HDR socklen_t.h)
-add_header(struct_sockaddr HDR struct_sockaddr.h DEPENDS .sa_family_t)
 add_header(struct_addrinfo HDR struct_addrinfo.h DEPENDS .socklen_t .struct_sockaddr)
 add_header(struct_sockaddr_in HDR struct_sockaddr_in.h DEPENDS .in_port_t .sa_family_t .struct_in_addr)
 add_header(
@@ -296,7 +297,11 @@ add_header(
     libc.include.llvm-libc-macros.stdint_macros
 )
 add_header(struct_sockaddr_un HDR struct_sockaddr_un.h DEPENDS .sa_family_t)
-add_header(struct_ifreq HDR struct_ifreq.h DEPENDS libc.include.llvm-libc-macros.net_if_macros .struct_sockaddr)
+add_type_header(
+  struct_ifreq
+  HDR
+    struct_ifreq.h
+)
 add_header(struct_if_nameindex HDR struct_if_nameindex.h)
 add_header(struct_iovec HDR struct_iovec.h DEPENDS .size_t)
 add_header(struct_linger HDR struct_linger.h)
diff --git a/libc/include/llvm-libc-types/linux/CMakeLists.txt b/libc/include/llvm-libc-types/linux/CMakeLists.txt
index d937ffe30e6e82..95a91af15848ff 100644
--- a/libc/include/llvm-libc-types/linux/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/linux/CMakeLists.txt
@@ -9,3 +9,12 @@ add_header(
   HDR
     struct_sysinfo.h
 )
+
+add_header(
+  struct_ifreq
+  HDR
+    struct_ifreq.h
+  DEPENDS
+    libc.include.llvm-libc-macros.net_if_macros
+    libc.include.llvm-libc-types.struct_sockaddr
+)
diff --git a/libc/include/llvm-libc-types/linux/struct_ifreq.h b/libc/include/llvm-libc-types/linux/struct_ifreq.h
new file mode 100644
index 00000000000000..44a7df05034166
--- /dev/null
+++ b/libc/include/llvm-libc-types/linux/struct_ifreq.h
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of struct ifreq for Linux.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_LINUX_STRUCT_IFREQ_H
+#define LLVM_LIBC_TYPES_LINUX_STRUCT_IFREQ_H
+
+#include "../../llvm-libc-macros/net-if-macros.h"
+#include "../struct_sockaddr.h"
+
+// Prevent the linux headers from defining this type.
+#define __UAPI_DEF_IF_IFREQ 0
+
+struct ifreq {
+  union {
+    char ifrn_name[IF_NAMESIZE];
+  } ifr_ifrn;
+
+  union {
+    struct sockaddr ifru_addr;
+    struct sockaddr ifru_dstaddr;
+    struct sockaddr ifru_broadaddr;
+    struct sockaddr ifru_netmask;
+    struct sockaddr ifru_hwaddr;
+    short int ifru_flags;
+    int ifru_ivalue;
+    int ifru_mtu;
+    char ifru_slave[IF_NAMESIZE];
+    char ifru_newname[IF_NAMESIZE];
+    char *ifru_data;
+  } ifr_ifru;
+};
+
+#define ifr_name ifr_ifrn.ifrn_name
+#define ifr_hwaddr ifr_ifru.ifru_hwaddr
+#define ifr_addr ifr_ifru.ifru_addr
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr
+#define ifr_netmask ifr_ifru.ifru_netmask
+#define ifr_flags ifr_ifru.ifru_flags
+#define ifr_metric ifr_ifru.ifru_ivalue
+#define ifr_mtu ifr_ifru.ifru_mtu
+#define ifr_slave ifr_ifru.ifru_slave
+#define ifr_data ifr_ifru.ifru_data
+#define ifr_ifindex ifr_ifru.ifru_ivalue
+#define ifr_bandwidth ifr_ifru.ifru_ivalue
+#define ifr_qlen ifr_ifru.ifru_ivalue
+#define ifr_newname ifr_ifru.ifru_newname
+
+#endif // LLVM_LIBC_TYPES_LINUX_STRUCT_IFREQ_H
diff --git a/libc/include/llvm-libc-types/struct_ifreq.h b/libc/include/llvm-libc-types/struct_ifreq.h
index b9ad182f978020..0a08f94eefb7c7 100644
--- a/libc/include/llvm-libc-types/struct_ifreq.h
+++ b/libc/include/llvm-libc-types/struct_ifreq.h
@@ -14,27 +14,8 @@
 #ifndef LLVM_LIBC_TYPES_STRUCT_IFREQ_H
 #define LLVM_LIBC_TYPES_STRUCT_IFREQ_H
 
-#include "../llvm-libc-macros/net-if-macros.h"
-#include "struct_sockaddr.h"
-
-struct ifreq {
-  char ifr_name[IF_NAMESIZE];
-  __extension__ union {
-    struct sockaddr ifr_hwaddr;
-    struct sockaddr ifr_addr;
-    struct sockaddr ifr_dstaddr;
-    struct sockaddr ifr_broadaddr;
-    struct sockaddr ifr_netmask;
-    short int ifr_flags;
-    int ifr_metric;
-    int ifr_mtu;
-    int ifr_ifindex;
-    int ifr_bandwidth;
-    int ifr_qlen;
-    char ifr_newname[IF_NAMESIZE];
-    char ifr_slave[IF_NAMESIZE];
-    char *ifr_data;
-  };
-};
+#if defined(__linux__)
+#include "linux/struct_ifreq.h"
+#endif
 
 #endif // LLVM_LIBC_TYPES_STRUCT_IFREQ_H
diff --git a/libc/test/include/CMakeLists.txt b/libc/test/include/CMakeLists.txt
index c2cfa416972c32..b2fb07f9c3def0 100644
--- a/libc/test/include/CMakeLists.txt
+++ b/libc/test/include/CMakeLists.txt
@@ -246,6 +246,8 @@ add_libc_test(
     libc.include.llvm-libc-macros.netinet_in_macros
 )
 
+add_subdirectory(net)
+
 add_libc_test(
   signbit_test
   SUITE
diff --git a/libc/test/include/net/CMakeLists.txt b/libc/test/include/net/CMakeLists.txt
new file mode 100644
index 00000000000000..8e285dd5b94c53
--- /dev/null
+++ b/libc/test/include/net/CMakeLists.txt
@@ -0,0 +1,4 @@
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+  add_subdirectory(${LIBC_TARGET_OS})
+endif()
+
diff --git a/libc/test/include/net/linux/CMakeLists.txt b/libc/test/include/net/linux/CMakeLists.txt
new file mode 100644
index 00000000000000..b83b00a822989c
--- /dev/null
+++ b/libc/test/include/net/linux/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_libc_test(
+  net_if_and_linux_if_test
+  SUITE
+    libc_include_tests
+  SRCS
+    net_if_and_linux_if_test.cpp
+  DEPENDS
+    libc.include.net_if
+)
+
diff --git a/libc/test/include/net/linux/net_if_and_linux_if_test.cpp b/libc/test/include/net/linux/net_if_and_linux_if_test.cpp
new file mode 100644
index 00000000000000..a1c56bf4e556b0
--- /dev/null
+++ b/libc/test/include/net/linux/net_if_and_linux_if_test.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for net/if.h and linux/if.h compatibility.
+///
+//===----------------------------------------------------------------------===//
+
+// Include our header first.
+#include <net/if.h>
+
+// And Linux header afterwards. The blank line prevents clang-format from
+// reordering these.
+#include <linux/if.h>
+
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcNetIfAndLinuxIfTest, LinuxIfAfterNetIf) {
+  // Test that <linux/if.h> can be included after <net/if.h>.
+  struct ifreq ifr;
+  (void)ifr;
+}

>From df73b77ae2747006e98f85360e02e024199af1db Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Thu, 24 Sep 2026 13:01:19 +0000
Subject: [PATCH 2/3] [libc] Add struct ifmap to struct ifreq definition

When struct ifreq was added, struct ifmap (and the corresponding
ifru_map union member) was omitted. On 64-bit Linux, struct ifmap is 24
bytes, while all other union members are at most 16 bytes. Leaving it
out made sizeof(struct ifreq) 32 bytes instead of 40, which mismatches
the kernel ABI and causes out-of-bounds kernel reads/writes in ioctls.
It also left the ifr_map macro from <linux/if.h> pointing to a
non-existent member when <net/if.h> was included first.

This patch adds struct ifmap (and __UAPI_DEF_IF_IFMAP) in a new header,
includes it in struct ifreq, and extends the test to verify that the
size and alignment of both structs match the kernel definitions in
<linux/if.h>.

Assisted-by: Gemini
---
 .../llvm-libc-types/linux/CMakeLists.txt      |  7 +++++
 .../llvm-libc-types/linux/struct_ifmap.h      | 29 +++++++++++++++++++
 .../llvm-libc-types/linux/struct_ifreq.h      |  3 ++
 libc/test/include/net/linux/CMakeLists.txt    |  2 ++
 .../include/net/linux/linux_if_helper.cpp     | 15 ++++++++++
 .../net/linux/net_if_and_linux_if_test.cpp    | 18 +++++++++---
 6 files changed, 70 insertions(+), 4 deletions(-)
 create mode 100644 libc/include/llvm-libc-types/linux/struct_ifmap.h
 create mode 100644 libc/test/include/net/linux/linux_if_helper.cpp

diff --git a/libc/include/llvm-libc-types/linux/CMakeLists.txt b/libc/include/llvm-libc-types/linux/CMakeLists.txt
index 95a91af15848ff..3796f597596d3a 100644
--- a/libc/include/llvm-libc-types/linux/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/linux/CMakeLists.txt
@@ -10,11 +10,18 @@ add_header(
     struct_sysinfo.h
 )
 
+add_header(
+  struct_ifmap
+  HDR
+    struct_ifmap.h
+)
+
 add_header(
   struct_ifreq
   HDR
     struct_ifreq.h
   DEPENDS
+    .struct_ifmap
     libc.include.llvm-libc-macros.net_if_macros
     libc.include.llvm-libc-types.struct_sockaddr
 )
diff --git a/libc/include/llvm-libc-types/linux/struct_ifmap.h b/libc/include/llvm-libc-types/linux/struct_ifmap.h
new file mode 100644
index 00000000000000..2546bb832743a2
--- /dev/null
+++ b/libc/include/llvm-libc-types/linux/struct_ifmap.h
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of struct ifmap for Linux.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_LINUX_STRUCT_IFMAP_H
+#define LLVM_LIBC_TYPES_LINUX_STRUCT_IFMAP_H
+
+// Prevent the linux headers from defining this type.
+#define __UAPI_DEF_IF_IFMAP 0
+
+struct ifmap {
+  unsigned long mem_start;
+  unsigned long mem_end;
+  unsigned short base_addr;
+  unsigned char irq;
+  unsigned char dma;
+  unsigned char port;
+};
+
+#endif // LLVM_LIBC_TYPES_LINUX_STRUCT_IFMAP_H
diff --git a/libc/include/llvm-libc-types/linux/struct_ifreq.h b/libc/include/llvm-libc-types/linux/struct_ifreq.h
index 44a7df05034166..eb0163e47a0fb2 100644
--- a/libc/include/llvm-libc-types/linux/struct_ifreq.h
+++ b/libc/include/llvm-libc-types/linux/struct_ifreq.h
@@ -16,6 +16,7 @@
 
 #include "../../llvm-libc-macros/net-if-macros.h"
 #include "../struct_sockaddr.h"
+#include "struct_ifmap.h"
 
 // Prevent the linux headers from defining this type.
 #define __UAPI_DEF_IF_IFREQ 0
@@ -34,6 +35,7 @@ struct ifreq {
     short int ifru_flags;
     int ifru_ivalue;
     int ifru_mtu;
+    struct ifmap ifru_map;
     char ifru_slave[IF_NAMESIZE];
     char ifru_newname[IF_NAMESIZE];
     char *ifru_data;
@@ -49,6 +51,7 @@ struct ifreq {
 #define ifr_flags ifr_ifru.ifru_flags
 #define ifr_metric ifr_ifru.ifru_ivalue
 #define ifr_mtu ifr_ifru.ifru_mtu
+#define ifr_map ifr_ifru.ifru_map
 #define ifr_slave ifr_ifru.ifru_slave
 #define ifr_data ifr_ifru.ifru_data
 #define ifr_ifindex ifr_ifru.ifru_ivalue
diff --git a/libc/test/include/net/linux/CMakeLists.txt b/libc/test/include/net/linux/CMakeLists.txt
index b83b00a822989c..29b63e2489611f 100644
--- a/libc/test/include/net/linux/CMakeLists.txt
+++ b/libc/test/include/net/linux/CMakeLists.txt
@@ -3,8 +3,10 @@ add_libc_test(
   SUITE
     libc_include_tests
   SRCS
+    linux_if_helper.cpp
     net_if_and_linux_if_test.cpp
   DEPENDS
     libc.include.net_if
+    libc.include.sys_socket
 )
 
diff --git a/libc/test/include/net/linux/linux_if_helper.cpp b/libc/test/include/net/linux/linux_if_helper.cpp
new file mode 100644
index 00000000000000..955f648967fe41
--- /dev/null
+++ b/libc/test/include/net/linux/linux_if_helper.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <linux/if.h>
+#include <stddef.h>
+
+extern const size_t LINUX_IFMAP_SIZE = sizeof(struct ifmap);
+extern const size_t LINUX_IFMAP_ALIGN = alignof(struct ifmap);
+extern const size_t LINUX_IFREQ_SIZE = sizeof(struct ifreq);
+extern const size_t LINUX_IFREQ_ALIGN = alignof(struct ifreq);
diff --git a/libc/test/include/net/linux/net_if_and_linux_if_test.cpp b/libc/test/include/net/linux/net_if_and_linux_if_test.cpp
index a1c56bf4e556b0..327cc3752f9a5a 100644
--- a/libc/test/include/net/linux/net_if_and_linux_if_test.cpp
+++ b/libc/test/include/net/linux/net_if_and_linux_if_test.cpp
@@ -11,17 +11,27 @@
 ///
 //===----------------------------------------------------------------------===//
 
-// Include our header first.
+// Test that <linux/if.h> can be included after <net/if.h>.  Include our header
+// first.
 #include <net/if.h>
 
 // And Linux header afterwards. The blank line prevents clang-format from
 // reordering these.
 #include <linux/if.h>
 
+#include <stddef.h>
 #include "test/UnitTest/Test.h"
 
+extern const size_t LINUX_IFMAP_SIZE;
+extern const size_t LINUX_IFMAP_ALIGN;
+extern const size_t LINUX_IFREQ_SIZE;
+extern const size_t LINUX_IFREQ_ALIGN;
+
 TEST(LlvmLibcNetIfAndLinuxIfTest, LinuxIfAfterNetIf) {
-  // Test that <linux/if.h> can be included after <net/if.h>.
-  struct ifreq ifr;
-  (void)ifr;
+  // Verify that our struct definitions match the size and alignment of the
+  // kernel UAPI definitions in <linux/if.h> (captured in linux_if_helper.cpp).
+  EXPECT_EQ(sizeof(struct ifmap), LINUX_IFMAP_SIZE);
+  EXPECT_EQ(alignof(struct ifmap), LINUX_IFMAP_ALIGN);
+  EXPECT_EQ(sizeof(struct ifreq), LINUX_IFREQ_SIZE);
+  EXPECT_EQ(alignof(struct ifreq), LINUX_IFREQ_ALIGN);
 }

>From 641986ee6750bd59b0e54b52216b721eb451519a Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Thu, 24 Sep 2026 13:18:46 +0000
Subject: [PATCH 3/3] format

---
 libc/test/include/net/linux/net_if_and_linux_if_test.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/test/include/net/linux/net_if_and_linux_if_test.cpp b/libc/test/include/net/linux/net_if_and_linux_if_test.cpp
index 327cc3752f9a5a..1e29ad07e9322e 100644
--- a/libc/test/include/net/linux/net_if_and_linux_if_test.cpp
+++ b/libc/test/include/net/linux/net_if_and_linux_if_test.cpp
@@ -19,8 +19,8 @@
 // reordering these.
 #include <linux/if.h>
 
-#include <stddef.h>
 #include "test/UnitTest/Test.h"
+#include <stddef.h>
 
 extern const size_t LINUX_IFMAP_SIZE;
 extern const size_t LINUX_IFMAP_ALIGN;



More information about the libc-commits mailing list