Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

docs: dev-tools: Add UAPI checker documentation

Add detailed documentation for scripts/check-uapi.sh.

Signed-off-by: John Moon <quic_johmoo@quicinc.com>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>

authored by

John Moon and committed by
Masahiro Yamada
8c88bc5b 1f7f31bf

+478
+477
Documentation/dev-tools/checkuapi.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0-only 2 + 3 + ============ 4 + UAPI Checker 5 + ============ 6 + 7 + The UAPI checker (``scripts/check-uapi.sh``) is a shell script which 8 + checks UAPI header files for userspace backwards-compatibility across 9 + the git tree. 10 + 11 + Options 12 + ======= 13 + 14 + This section will describe the options with which ``check-uapi.sh`` 15 + can be run. 16 + 17 + Usage:: 18 + 19 + check-uapi.sh [-b BASE_REF] [-p PAST_REF] [-j N] [-l ERROR_LOG] [-i] [-q] [-v] 20 + 21 + Available options:: 22 + 23 + -b BASE_REF Base git reference to use for comparison. If unspecified or empty, 24 + will use any dirty changes in tree to UAPI files. If there are no 25 + dirty changes, HEAD will be used. 26 + -p PAST_REF Compare BASE_REF to PAST_REF (e.g. -p v6.1). If unspecified or empty, 27 + will use BASE_REF^1. Must be an ancestor of BASE_REF. Only headers 28 + that exist on PAST_REF will be checked for compatibility. 29 + -j JOBS Number of checks to run in parallel (default: number of CPU cores). 30 + -l ERROR_LOG Write error log to file (default: no error log is generated). 31 + -i Ignore ambiguous changes that may or may not break UAPI compatibility. 32 + -q Quiet operation. 33 + -v Verbose operation (print more information about each header being checked). 34 + 35 + Environmental args:: 36 + 37 + ABIDIFF Custom path to abidiff binary 38 + CC C compiler (default is "gcc") 39 + ARCH Target architecture of C compiler (default is host arch) 40 + 41 + Exit codes:: 42 + 43 + 0) Success 44 + 1) ABI difference detected 45 + 2) Prerequisite not met 46 + 47 + Examples 48 + ======== 49 + 50 + Basic Usage 51 + ----------- 52 + 53 + First, let's try making a change to a UAPI header file that obviously 54 + won't break userspace:: 55 + 56 + cat << 'EOF' | patch -l -p1 57 + --- a/include/uapi/linux/acct.h 58 + +++ b/include/uapi/linux/acct.h 59 + @@ -21,7 +21,9 @@ 60 + #include <asm/param.h> 61 + #include <asm/byteorder.h> 62 + 63 + -/* 64 + +#define FOO 65 + + 66 + +/* 67 + * comp_t is a 16-bit "floating" point number with a 3-bit base 8 68 + * exponent and a 13-bit fraction. 69 + * comp2_t is 24-bit with 5-bit base 2 exponent and 20 bit fraction 70 + diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h 71 + EOF 72 + 73 + Now, let's use the script to validate:: 74 + 75 + % ./scripts/check-uapi.sh 76 + Installing user-facing UAPI headers from dirty tree... OK 77 + Installing user-facing UAPI headers from HEAD... OK 78 + Checking changes to UAPI headers between HEAD and dirty tree... 79 + All 912 UAPI headers compatible with x86 appear to be backwards compatible 80 + 81 + Let's add another change that *might* break userspace:: 82 + 83 + cat << 'EOF' | patch -l -p1 84 + --- a/include/uapi/linux/bpf.h 85 + +++ b/include/uapi/linux/bpf.h 86 + @@ -74,7 +74,7 @@ struct bpf_insn { 87 + __u8 dst_reg:4; /* dest register */ 88 + __u8 src_reg:4; /* source register */ 89 + __s16 off; /* signed offset */ 90 + - __s32 imm; /* signed immediate constant */ 91 + + __u32 imm; /* unsigned immediate constant */ 92 + }; 93 + 94 + /* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */ 95 + EOF 96 + 97 + The script will catch this:: 98 + 99 + % ./scripts/check-uapi.sh 100 + Installing user-facing UAPI headers from dirty tree... OK 101 + Installing user-facing UAPI headers from HEAD... OK 102 + Checking changes to UAPI headers between HEAD and dirty tree... 103 + ==== ABI differences detected in include/linux/bpf.h from HEAD -> dirty tree ==== 104 + [C] 'struct bpf_insn' changed: 105 + type size hasn't changed 106 + 1 data member change: 107 + type of '__s32 imm' changed: 108 + typedef name changed from __s32 to __u32 at int-ll64.h:27:1 109 + underlying type 'int' changed: 110 + type name changed from 'int' to 'unsigned int' 111 + type size hasn't changed 112 + ================================================================================== 113 + 114 + error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible 115 + 116 + In this case, the script is reporting the type change because it could 117 + break a userspace program that passes in a negative number. Now, let's 118 + say you know that no userspace program could possibly be using a negative 119 + value in ``imm``, so changing to an unsigned type there shouldn't hurt 120 + anything. You can pass the ``-i`` flag to the script to ignore changes 121 + in which the userspace backwards compatibility is ambiguous:: 122 + 123 + % ./scripts/check-uapi.sh -i 124 + Installing user-facing UAPI headers from dirty tree... OK 125 + Installing user-facing UAPI headers from HEAD... OK 126 + Checking changes to UAPI headers between HEAD and dirty tree... 127 + All 912 UAPI headers compatible with x86 appear to be backwards compatible 128 + 129 + Now, let's make a similar change that *will* break userspace:: 130 + 131 + cat << 'EOF' | patch -l -p1 132 + --- a/include/uapi/linux/bpf.h 133 + +++ b/include/uapi/linux/bpf.h 134 + @@ -71,8 +71,8 @@ enum { 135 + 136 + struct bpf_insn { 137 + __u8 code; /* opcode */ 138 + - __u8 dst_reg:4; /* dest register */ 139 + __u8 src_reg:4; /* source register */ 140 + + __u8 dst_reg:4; /* dest register */ 141 + __s16 off; /* signed offset */ 142 + __s32 imm; /* signed immediate constant */ 143 + }; 144 + EOF 145 + 146 + Since we're re-ordering an existing struct member, there's no ambiguity, 147 + and the script will report the breakage even if you pass ``-i``:: 148 + 149 + % ./scripts/check-uapi.sh -i 150 + Installing user-facing UAPI headers from dirty tree... OK 151 + Installing user-facing UAPI headers from HEAD... OK 152 + Checking changes to UAPI headers between HEAD and dirty tree... 153 + ==== ABI differences detected in include/linux/bpf.h from HEAD -> dirty tree ==== 154 + [C] 'struct bpf_insn' changed: 155 + type size hasn't changed 156 + 2 data member changes: 157 + '__u8 dst_reg' offset changed from 8 to 12 (in bits) (by +4 bits) 158 + '__u8 src_reg' offset changed from 12 to 8 (in bits) (by -4 bits) 159 + ================================================================================== 160 + 161 + error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible 162 + 163 + Let's commit the breaking change, then commit the innocuous change:: 164 + 165 + % git commit -m 'Breaking UAPI change' include/uapi/linux/bpf.h 166 + [detached HEAD f758e574663a] Breaking UAPI change 167 + 1 file changed, 1 insertion(+), 1 deletion(-) 168 + % git commit -m 'Innocuous UAPI change' include/uapi/linux/acct.h 169 + [detached HEAD 2e87df769081] Innocuous UAPI change 170 + 1 file changed, 3 insertions(+), 1 deletion(-) 171 + 172 + Now, let's run the script again with no arguments:: 173 + 174 + % ./scripts/check-uapi.sh 175 + Installing user-facing UAPI headers from HEAD... OK 176 + Installing user-facing UAPI headers from HEAD^1... OK 177 + Checking changes to UAPI headers between HEAD^1 and HEAD... 178 + All 912 UAPI headers compatible with x86 appear to be backwards compatible 179 + 180 + It doesn't catch any breaking change because, by default, it only 181 + compares ``HEAD`` to ``HEAD^1``. The breaking change was committed on 182 + ``HEAD~2``. If we wanted the search scope to go back further, we'd have to 183 + use the ``-p`` option to pass a different past reference. In this case, 184 + let's pass ``-p HEAD~2`` to the script so it checks UAPI changes between 185 + ``HEAD~2`` and ``HEAD``:: 186 + 187 + % ./scripts/check-uapi.sh -p HEAD~2 188 + Installing user-facing UAPI headers from HEAD... OK 189 + Installing user-facing UAPI headers from HEAD~2... OK 190 + Checking changes to UAPI headers between HEAD~2 and HEAD... 191 + ==== ABI differences detected in include/linux/bpf.h from HEAD~2 -> HEAD ==== 192 + [C] 'struct bpf_insn' changed: 193 + type size hasn't changed 194 + 2 data member changes: 195 + '__u8 dst_reg' offset changed from 8 to 12 (in bits) (by +4 bits) 196 + '__u8 src_reg' offset changed from 12 to 8 (in bits) (by -4 bits) 197 + ============================================================================== 198 + 199 + error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible 200 + 201 + Alternatively, we could have also run with ``-b HEAD~``. This would set the 202 + base reference to ``HEAD~`` so then the script would compare it to ``HEAD~^1``. 203 + 204 + Architecture-specific Headers 205 + ----------------------------- 206 + 207 + Consider this change:: 208 + 209 + cat << 'EOF' | patch -l -p1 210 + --- a/arch/arm64/include/uapi/asm/sigcontext.h 211 + +++ b/arch/arm64/include/uapi/asm/sigcontext.h 212 + @@ -70,6 +70,7 @@ struct sigcontext { 213 + struct _aarch64_ctx { 214 + __u32 magic; 215 + __u32 size; 216 + + __u32 new_var; 217 + }; 218 + 219 + #define FPSIMD_MAGIC 0x46508001 220 + EOF 221 + 222 + This is a change to an arm64-specific UAPI header file. In this example, I'm 223 + running the script from an x86 machine with an x86 compiler, so, by default, 224 + the script only checks x86-compatible UAPI header files:: 225 + 226 + % ./scripts/check-uapi.sh 227 + Installing user-facing UAPI headers from dirty tree... OK 228 + Installing user-facing UAPI headers from HEAD... OK 229 + No changes to UAPI headers were applied between HEAD and dirty tree 230 + 231 + With an x86 compiler, we can't check header files in ``arch/arm64``, so the 232 + script doesn't even try. 233 + 234 + If we want to check the header file, we'll have to use an arm64 compiler and 235 + set ``ARCH`` accordingly:: 236 + 237 + % CC=aarch64-linux-gnu-gcc ARCH=arm64 ./scripts/check-uapi.sh 238 + Installing user-facing UAPI headers from dirty tree... OK 239 + Installing user-facing UAPI headers from HEAD... OK 240 + Checking changes to UAPI headers between HEAD and dirty tree... 241 + ==== ABI differences detected in include/asm/sigcontext.h from HEAD -> dirty tree ==== 242 + [C] 'struct _aarch64_ctx' changed: 243 + type size changed from 64 to 96 (in bits) 244 + 1 data member insertion: 245 + '__u32 new_var', at offset 64 (in bits) at sigcontext.h:73:1 246 + -- snip -- 247 + [C] 'struct zt_context' changed: 248 + type size changed from 128 to 160 (in bits) 249 + 2 data member changes (1 filtered): 250 + '__u16 nregs' offset changed from 64 to 96 (in bits) (by +32 bits) 251 + '__u16 __reserved[3]' offset changed from 80 to 112 (in bits) (by +32 bits) 252 + ======================================================================================= 253 + 254 + error - 1/884 UAPI headers compatible with arm64 appear _not_ to be backwards compatible 255 + 256 + We can see with ``ARCH`` and ``CC`` set properly for the file, the ABI 257 + change is reported properly. Also notice that the total number of UAPI 258 + header files checked by the script changes. This is because the number 259 + of headers installed for arm64 platforms is different than x86. 260 + 261 + Cross-Dependency Breakages 262 + -------------------------- 263 + 264 + Consider this change:: 265 + 266 + cat << 'EOF' | patch -l -p1 267 + --- a/include/uapi/linux/types.h 268 + +++ b/include/uapi/linux/types.h 269 + @@ -52,7 +52,7 @@ typedef __u32 __bitwise __wsum; 270 + #define __aligned_be64 __be64 __attribute__((aligned(8))) 271 + #define __aligned_le64 __le64 __attribute__((aligned(8))) 272 + 273 + -typedef unsigned __bitwise __poll_t; 274 + +typedef unsigned short __bitwise __poll_t; 275 + 276 + #endif /* __ASSEMBLY__ */ 277 + #endif /* _UAPI_LINUX_TYPES_H */ 278 + EOF 279 + 280 + Here, we're changing a ``typedef`` in ``types.h``. This doesn't break 281 + a UAPI in ``types.h``, but other UAPIs in the tree may break due to 282 + this change:: 283 + 284 + % ./scripts/check-uapi.sh 285 + Installing user-facing UAPI headers from dirty tree... OK 286 + Installing user-facing UAPI headers from HEAD... OK 287 + Checking changes to UAPI headers between HEAD and dirty tree... 288 + ==== ABI differences detected in include/linux/eventpoll.h from HEAD -> dirty tree ==== 289 + [C] 'struct epoll_event' changed: 290 + type size changed from 96 to 80 (in bits) 291 + 2 data member changes: 292 + type of '__poll_t events' changed: 293 + underlying type 'unsigned int' changed: 294 + type name changed from 'unsigned int' to 'unsigned short int' 295 + type size changed from 32 to 16 (in bits) 296 + '__u64 data' offset changed from 32 to 16 (in bits) (by -16 bits) 297 + ======================================================================================== 298 + include/linux/eventpoll.h did not change between HEAD and dirty tree... 299 + It's possible a change to one of the headers it includes caused this error: 300 + #include <linux/fcntl.h> 301 + #include <linux/types.h> 302 + 303 + Note that the script noticed the failing header file did not change, 304 + so it assumes one of its includes must have caused the breakage. Indeed, 305 + we can see ``linux/types.h`` is used from ``eventpoll.h``. 306 + 307 + UAPI Header Removals 308 + -------------------- 309 + 310 + Consider this change:: 311 + 312 + cat << 'EOF' | patch -l -p1 313 + diff --git a/include/uapi/asm-generic/Kbuild b/include/uapi/asm-generic/Kbuild 314 + index ebb180aac74e..a9c88b0a8b3b 100644 315 + --- a/include/uapi/asm-generic/Kbuild 316 + +++ b/include/uapi/asm-generic/Kbuild 317 + @@ -31,6 +31,6 @@ mandatory-y += stat.h 318 + mandatory-y += statfs.h 319 + mandatory-y += swab.h 320 + mandatory-y += termbits.h 321 + -mandatory-y += termios.h 322 + +#mandatory-y += termios.h 323 + mandatory-y += types.h 324 + mandatory-y += unistd.h 325 + EOF 326 + 327 + This script removes a UAPI header file from the install list. Let's run 328 + the script:: 329 + 330 + % ./scripts/check-uapi.sh 331 + Installing user-facing UAPI headers from dirty tree... OK 332 + Installing user-facing UAPI headers from HEAD... OK 333 + Checking changes to UAPI headers between HEAD and dirty tree... 334 + ==== UAPI header include/asm/termios.h was removed between HEAD and dirty tree ==== 335 + 336 + error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible 337 + 338 + Removing a UAPI header is considered a breaking change, and the script 339 + will flag it as such. 340 + 341 + Checking Historic UAPI Compatibility 342 + ------------------------------------ 343 + 344 + You can use the ``-b`` and ``-p`` options to examine different chunks of your 345 + git tree. For example, to check all changed UAPI header files between tags 346 + v6.0 and v6.1, you'd run:: 347 + 348 + % ./scripts/check-uapi.sh -b v6.1 -p v6.0 349 + Installing user-facing UAPI headers from v6.1... OK 350 + Installing user-facing UAPI headers from v6.0... OK 351 + Checking changes to UAPI headers between v6.0 and v6.1... 352 + 353 + --- snip --- 354 + error - 37/907 UAPI headers compatible with x86 appear _not_ to be backwards compatible 355 + 356 + Note: Before v5.3, a header file needed by the script is not present, 357 + so the script is unable to check changes before then. 358 + 359 + You'll notice that the script detected many UAPI changes that are not 360 + backwards compatible. Knowing that kernel UAPIs are supposed to be stable 361 + forever, this is an alarming result. This brings us to the next section: 362 + caveats. 363 + 364 + Caveats 365 + ======= 366 + 367 + The UAPI checker makes no assumptions about the author's intention, so some 368 + types of changes may be flagged even though they intentionally break UAPI. 369 + 370 + Removals For Refactoring or Deprecation 371 + --------------------------------------- 372 + 373 + Sometimes drivers for very old hardware are removed, such as in this example:: 374 + 375 + % ./scripts/check-uapi.sh -b ba47652ba655 376 + Installing user-facing UAPI headers from ba47652ba655... OK 377 + Installing user-facing UAPI headers from ba47652ba655^1... OK 378 + Checking changes to UAPI headers between ba47652ba655^1 and ba47652ba655... 379 + ==== UAPI header include/linux/meye.h was removed between ba47652ba655^1 and ba47652ba655 ==== 380 + 381 + error - 1/910 UAPI headers compatible with x86 appear _not_ to be backwards compatible 382 + 383 + The script will always flag removals (even if they're intentional). 384 + 385 + Struct Expansions 386 + ----------------- 387 + 388 + Depending on how a structure is handled in kernelspace, a change which 389 + expands a struct could be non-breaking. 390 + 391 + If a struct is used as the argument to an ioctl, then the kernel driver 392 + must be able to handle ioctl commands of any size. Beyond that, you need 393 + to be careful when copying data from the user. Say, for example, that 394 + ``struct foo`` is changed like this:: 395 + 396 + struct foo { 397 + __u64 a; /* added in version 1 */ 398 + + __u32 b; /* added in version 2 */ 399 + + __u32 c; /* added in version 2 */ 400 + } 401 + 402 + By default, the script will flag this kind of change for further review:: 403 + 404 + [C] 'struct foo' changed: 405 + type size changed from 64 to 128 (in bits) 406 + 2 data member insertions: 407 + '__u32 b', at offset 64 (in bits) 408 + '__u32 c', at offset 96 (in bits) 409 + 410 + However, it is possible that this change was made safely. 411 + 412 + If a userspace program was built with version 1, it will think 413 + ``sizeof(struct foo)`` is 8. That size will be encoded in the 414 + ioctl value that gets sent to the kernel. If the kernel is built 415 + with version 2, it will think the ``sizeof(struct foo)`` is 16. 416 + 417 + The kernel can use the ``_IOC_SIZE`` macro to get the size encoded 418 + in the ioctl code that the user passed in and then use 419 + ``copy_struct_from_user()`` to safely copy the value:: 420 + 421 + int handle_ioctl(unsigned long cmd, unsigned long arg) 422 + { 423 + switch _IOC_NR(cmd) { 424 + 0x01: { 425 + struct foo my_cmd; /* size 16 in the kernel */ 426 + 427 + ret = copy_struct_from_user(&my_cmd, arg, sizeof(struct foo), _IOC_SIZE(cmd)); 428 + ... 429 + 430 + ``copy_struct_from_user`` will zero the struct in the kernel and then copy 431 + only the bytes passed in from the user (leaving new members zeroized). 432 + If the user passed in a larger struct, the extra members are ignored. 433 + 434 + If you know this situation is accounted for in the kernel code, you can 435 + pass ``-i`` to the script, and struct expansions like this will be ignored. 436 + 437 + Flex Array Migration 438 + -------------------- 439 + 440 + While the script handles expansion into an existing flex array, it does 441 + still flag initial migration to flex arrays from 1-element fake flex 442 + arrays. For example:: 443 + 444 + struct foo { 445 + __u32 x; 446 + - __u32 flex[1]; /* fake flex */ 447 + + __u32 flex[]; /* real flex */ 448 + }; 449 + 450 + This change would be flagged by the script:: 451 + 452 + [C] 'struct foo' changed: 453 + type size changed from 64 to 32 (in bits) 454 + 1 data member change: 455 + type of '__u32 flex[1]' changed: 456 + type name changed from '__u32[1]' to '__u32[]' 457 + array type size changed from 32 to 'unknown' 458 + array type subrange 1 changed length from 1 to 'unknown' 459 + 460 + At this time, there's no way to filter these types of changes, so be 461 + aware of this possible false positive. 462 + 463 + Summary 464 + ------- 465 + 466 + While many types of false positives are filtered out by the script, 467 + it's possible there are some cases where the script flags a change 468 + which does not break UAPI. It's also possible a change which *does* 469 + break userspace would not be flagged by this script. While the script 470 + has been run on much of the kernel history, there could still be corner 471 + cases that are not accounted for. 472 + 473 + The intention is for this script to be used as a quick check for 474 + maintainers or automated tooling, not as the end-all authority on 475 + patch compatibility. It's best to remember: use your best judgment 476 + (and ideally a unit test in userspace) to make sure your UAPI changes 477 + are backwards-compatible!
+1
Documentation/dev-tools/index.rst
··· 34 34 kselftest 35 35 kunit/index 36 36 ktap 37 + checkuapi 37 38 38 39 39 40 .. only:: subproject and html