···11+.. SPDX-License-Identifier: GPL-2.0-only22+33+============44+UAPI Checker55+============66+77+The UAPI checker (``scripts/check-uapi.sh``) is a shell script which88+checks UAPI header files for userspace backwards-compatibility across99+the git tree.1010+1111+Options1212+=======1313+1414+This section will describe the options with which ``check-uapi.sh``1515+can be run.1616+1717+Usage::1818+1919+ check-uapi.sh [-b BASE_REF] [-p PAST_REF] [-j N] [-l ERROR_LOG] [-i] [-q] [-v]2020+2121+Available options::2222+2323+ -b BASE_REF Base git reference to use for comparison. If unspecified or empty,2424+ will use any dirty changes in tree to UAPI files. If there are no2525+ dirty changes, HEAD will be used.2626+ -p PAST_REF Compare BASE_REF to PAST_REF (e.g. -p v6.1). If unspecified or empty,2727+ will use BASE_REF^1. Must be an ancestor of BASE_REF. Only headers2828+ that exist on PAST_REF will be checked for compatibility.2929+ -j JOBS Number of checks to run in parallel (default: number of CPU cores).3030+ -l ERROR_LOG Write error log to file (default: no error log is generated).3131+ -i Ignore ambiguous changes that may or may not break UAPI compatibility.3232+ -q Quiet operation.3333+ -v Verbose operation (print more information about each header being checked).3434+3535+Environmental args::3636+3737+ ABIDIFF Custom path to abidiff binary3838+ CC C compiler (default is "gcc")3939+ ARCH Target architecture of C compiler (default is host arch)4040+4141+Exit codes::4242+4343+ 0) Success4444+ 1) ABI difference detected4545+ 2) Prerequisite not met4646+4747+Examples4848+========4949+5050+Basic Usage5151+-----------5252+5353+First, let's try making a change to a UAPI header file that obviously5454+won't break userspace::5555+5656+ cat << 'EOF' | patch -l -p15757+ --- a/include/uapi/linux/acct.h5858+ +++ b/include/uapi/linux/acct.h5959+ @@ -21,7 +21,9 @@6060+ #include <asm/param.h>6161+ #include <asm/byteorder.h>6262+6363+ -/*6464+ +#define FOO6565+ +6666+ +/*6767+ * comp_t is a 16-bit "floating" point number with a 3-bit base 86868+ * exponent and a 13-bit fraction.6969+ * comp2_t is 24-bit with 5-bit base 2 exponent and 20 bit fraction7070+ diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h7171+ EOF7272+7373+Now, let's use the script to validate::7474+7575+ % ./scripts/check-uapi.sh7676+ Installing user-facing UAPI headers from dirty tree... OK7777+ Installing user-facing UAPI headers from HEAD... OK7878+ Checking changes to UAPI headers between HEAD and dirty tree...7979+ All 912 UAPI headers compatible with x86 appear to be backwards compatible8080+8181+Let's add another change that *might* break userspace::8282+8383+ cat << 'EOF' | patch -l -p18484+ --- a/include/uapi/linux/bpf.h8585+ +++ b/include/uapi/linux/bpf.h8686+ @@ -74,7 +74,7 @@ struct bpf_insn {8787+ __u8 dst_reg:4; /* dest register */8888+ __u8 src_reg:4; /* source register */8989+ __s16 off; /* signed offset */9090+ - __s32 imm; /* signed immediate constant */9191+ + __u32 imm; /* unsigned immediate constant */9292+ };9393+9494+ /* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */9595+ EOF9696+9797+The script will catch this::9898+9999+ % ./scripts/check-uapi.sh100100+ Installing user-facing UAPI headers from dirty tree... OK101101+ Installing user-facing UAPI headers from HEAD... OK102102+ Checking changes to UAPI headers between HEAD and dirty tree...103103+ ==== ABI differences detected in include/linux/bpf.h from HEAD -> dirty tree ====104104+ [C] 'struct bpf_insn' changed:105105+ type size hasn't changed106106+ 1 data member change:107107+ type of '__s32 imm' changed:108108+ typedef name changed from __s32 to __u32 at int-ll64.h:27:1109109+ underlying type 'int' changed:110110+ type name changed from 'int' to 'unsigned int'111111+ type size hasn't changed112112+ ==================================================================================113113+114114+ error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible115115+116116+In this case, the script is reporting the type change because it could117117+break a userspace program that passes in a negative number. Now, let's118118+say you know that no userspace program could possibly be using a negative119119+value in ``imm``, so changing to an unsigned type there shouldn't hurt120120+anything. You can pass the ``-i`` flag to the script to ignore changes121121+in which the userspace backwards compatibility is ambiguous::122122+123123+ % ./scripts/check-uapi.sh -i124124+ Installing user-facing UAPI headers from dirty tree... OK125125+ Installing user-facing UAPI headers from HEAD... OK126126+ Checking changes to UAPI headers between HEAD and dirty tree...127127+ All 912 UAPI headers compatible with x86 appear to be backwards compatible128128+129129+Now, let's make a similar change that *will* break userspace::130130+131131+ cat << 'EOF' | patch -l -p1132132+ --- a/include/uapi/linux/bpf.h133133+ +++ b/include/uapi/linux/bpf.h134134+ @@ -71,8 +71,8 @@ enum {135135+136136+ struct bpf_insn {137137+ __u8 code; /* opcode */138138+ - __u8 dst_reg:4; /* dest register */139139+ __u8 src_reg:4; /* source register */140140+ + __u8 dst_reg:4; /* dest register */141141+ __s16 off; /* signed offset */142142+ __s32 imm; /* signed immediate constant */143143+ };144144+ EOF145145+146146+Since we're re-ordering an existing struct member, there's no ambiguity,147147+and the script will report the breakage even if you pass ``-i``::148148+149149+ % ./scripts/check-uapi.sh -i150150+ Installing user-facing UAPI headers from dirty tree... OK151151+ Installing user-facing UAPI headers from HEAD... OK152152+ Checking changes to UAPI headers between HEAD and dirty tree...153153+ ==== ABI differences detected in include/linux/bpf.h from HEAD -> dirty tree ====154154+ [C] 'struct bpf_insn' changed:155155+ type size hasn't changed156156+ 2 data member changes:157157+ '__u8 dst_reg' offset changed from 8 to 12 (in bits) (by +4 bits)158158+ '__u8 src_reg' offset changed from 12 to 8 (in bits) (by -4 bits)159159+ ==================================================================================160160+161161+ error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible162162+163163+Let's commit the breaking change, then commit the innocuous change::164164+165165+ % git commit -m 'Breaking UAPI change' include/uapi/linux/bpf.h166166+ [detached HEAD f758e574663a] Breaking UAPI change167167+ 1 file changed, 1 insertion(+), 1 deletion(-)168168+ % git commit -m 'Innocuous UAPI change' include/uapi/linux/acct.h169169+ [detached HEAD 2e87df769081] Innocuous UAPI change170170+ 1 file changed, 3 insertions(+), 1 deletion(-)171171+172172+Now, let's run the script again with no arguments::173173+174174+ % ./scripts/check-uapi.sh175175+ Installing user-facing UAPI headers from HEAD... OK176176+ Installing user-facing UAPI headers from HEAD^1... OK177177+ Checking changes to UAPI headers between HEAD^1 and HEAD...178178+ All 912 UAPI headers compatible with x86 appear to be backwards compatible179179+180180+It doesn't catch any breaking change because, by default, it only181181+compares ``HEAD`` to ``HEAD^1``. The breaking change was committed on182182+``HEAD~2``. If we wanted the search scope to go back further, we'd have to183183+use the ``-p`` option to pass a different past reference. In this case,184184+let's pass ``-p HEAD~2`` to the script so it checks UAPI changes between185185+``HEAD~2`` and ``HEAD``::186186+187187+ % ./scripts/check-uapi.sh -p HEAD~2188188+ Installing user-facing UAPI headers from HEAD... OK189189+ Installing user-facing UAPI headers from HEAD~2... OK190190+ Checking changes to UAPI headers between HEAD~2 and HEAD...191191+ ==== ABI differences detected in include/linux/bpf.h from HEAD~2 -> HEAD ====192192+ [C] 'struct bpf_insn' changed:193193+ type size hasn't changed194194+ 2 data member changes:195195+ '__u8 dst_reg' offset changed from 8 to 12 (in bits) (by +4 bits)196196+ '__u8 src_reg' offset changed from 12 to 8 (in bits) (by -4 bits)197197+ ==============================================================================198198+199199+ error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible200200+201201+Alternatively, we could have also run with ``-b HEAD~``. This would set the202202+base reference to ``HEAD~`` so then the script would compare it to ``HEAD~^1``.203203+204204+Architecture-specific Headers205205+-----------------------------206206+207207+Consider this change::208208+209209+ cat << 'EOF' | patch -l -p1210210+ --- a/arch/arm64/include/uapi/asm/sigcontext.h211211+ +++ b/arch/arm64/include/uapi/asm/sigcontext.h212212+ @@ -70,6 +70,7 @@ struct sigcontext {213213+ struct _aarch64_ctx {214214+ __u32 magic;215215+ __u32 size;216216+ + __u32 new_var;217217+ };218218+219219+ #define FPSIMD_MAGIC 0x46508001220220+ EOF221221+222222+This is a change to an arm64-specific UAPI header file. In this example, I'm223223+running the script from an x86 machine with an x86 compiler, so, by default,224224+the script only checks x86-compatible UAPI header files::225225+226226+ % ./scripts/check-uapi.sh227227+ Installing user-facing UAPI headers from dirty tree... OK228228+ Installing user-facing UAPI headers from HEAD... OK229229+ No changes to UAPI headers were applied between HEAD and dirty tree230230+231231+With an x86 compiler, we can't check header files in ``arch/arm64``, so the232232+script doesn't even try.233233+234234+If we want to check the header file, we'll have to use an arm64 compiler and235235+set ``ARCH`` accordingly::236236+237237+ % CC=aarch64-linux-gnu-gcc ARCH=arm64 ./scripts/check-uapi.sh238238+ Installing user-facing UAPI headers from dirty tree... OK239239+ Installing user-facing UAPI headers from HEAD... OK240240+ Checking changes to UAPI headers between HEAD and dirty tree...241241+ ==== ABI differences detected in include/asm/sigcontext.h from HEAD -> dirty tree ====242242+ [C] 'struct _aarch64_ctx' changed:243243+ type size changed from 64 to 96 (in bits)244244+ 1 data member insertion:245245+ '__u32 new_var', at offset 64 (in bits) at sigcontext.h:73:1246246+ -- snip --247247+ [C] 'struct zt_context' changed:248248+ type size changed from 128 to 160 (in bits)249249+ 2 data member changes (1 filtered):250250+ '__u16 nregs' offset changed from 64 to 96 (in bits) (by +32 bits)251251+ '__u16 __reserved[3]' offset changed from 80 to 112 (in bits) (by +32 bits)252252+ =======================================================================================253253+254254+ error - 1/884 UAPI headers compatible with arm64 appear _not_ to be backwards compatible255255+256256+We can see with ``ARCH`` and ``CC`` set properly for the file, the ABI257257+change is reported properly. Also notice that the total number of UAPI258258+header files checked by the script changes. This is because the number259259+of headers installed for arm64 platforms is different than x86.260260+261261+Cross-Dependency Breakages262262+--------------------------263263+264264+Consider this change::265265+266266+ cat << 'EOF' | patch -l -p1267267+ --- a/include/uapi/linux/types.h268268+ +++ b/include/uapi/linux/types.h269269+ @@ -52,7 +52,7 @@ typedef __u32 __bitwise __wsum;270270+ #define __aligned_be64 __be64 __attribute__((aligned(8)))271271+ #define __aligned_le64 __le64 __attribute__((aligned(8)))272272+273273+ -typedef unsigned __bitwise __poll_t;274274+ +typedef unsigned short __bitwise __poll_t;275275+276276+ #endif /* __ASSEMBLY__ */277277+ #endif /* _UAPI_LINUX_TYPES_H */278278+ EOF279279+280280+Here, we're changing a ``typedef`` in ``types.h``. This doesn't break281281+a UAPI in ``types.h``, but other UAPIs in the tree may break due to282282+this change::283283+284284+ % ./scripts/check-uapi.sh285285+ Installing user-facing UAPI headers from dirty tree... OK286286+ Installing user-facing UAPI headers from HEAD... OK287287+ Checking changes to UAPI headers between HEAD and dirty tree...288288+ ==== ABI differences detected in include/linux/eventpoll.h from HEAD -> dirty tree ====289289+ [C] 'struct epoll_event' changed:290290+ type size changed from 96 to 80 (in bits)291291+ 2 data member changes:292292+ type of '__poll_t events' changed:293293+ underlying type 'unsigned int' changed:294294+ type name changed from 'unsigned int' to 'unsigned short int'295295+ type size changed from 32 to 16 (in bits)296296+ '__u64 data' offset changed from 32 to 16 (in bits) (by -16 bits)297297+ ========================================================================================298298+ include/linux/eventpoll.h did not change between HEAD and dirty tree...299299+ It's possible a change to one of the headers it includes caused this error:300300+ #include <linux/fcntl.h>301301+ #include <linux/types.h>302302+303303+Note that the script noticed the failing header file did not change,304304+so it assumes one of its includes must have caused the breakage. Indeed,305305+we can see ``linux/types.h`` is used from ``eventpoll.h``.306306+307307+UAPI Header Removals308308+--------------------309309+310310+Consider this change::311311+312312+ cat << 'EOF' | patch -l -p1313313+ diff --git a/include/uapi/asm-generic/Kbuild b/include/uapi/asm-generic/Kbuild314314+ index ebb180aac74e..a9c88b0a8b3b 100644315315+ --- a/include/uapi/asm-generic/Kbuild316316+ +++ b/include/uapi/asm-generic/Kbuild317317+ @@ -31,6 +31,6 @@ mandatory-y += stat.h318318+ mandatory-y += statfs.h319319+ mandatory-y += swab.h320320+ mandatory-y += termbits.h321321+ -mandatory-y += termios.h322322+ +#mandatory-y += termios.h323323+ mandatory-y += types.h324324+ mandatory-y += unistd.h325325+ EOF326326+327327+This script removes a UAPI header file from the install list. Let's run328328+the script::329329+330330+ % ./scripts/check-uapi.sh331331+ Installing user-facing UAPI headers from dirty tree... OK332332+ Installing user-facing UAPI headers from HEAD... OK333333+ Checking changes to UAPI headers between HEAD and dirty tree...334334+ ==== UAPI header include/asm/termios.h was removed between HEAD and dirty tree ====335335+336336+ error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible337337+338338+Removing a UAPI header is considered a breaking change, and the script339339+will flag it as such.340340+341341+Checking Historic UAPI Compatibility342342+------------------------------------343343+344344+You can use the ``-b`` and ``-p`` options to examine different chunks of your345345+git tree. For example, to check all changed UAPI header files between tags346346+v6.0 and v6.1, you'd run::347347+348348+ % ./scripts/check-uapi.sh -b v6.1 -p v6.0349349+ Installing user-facing UAPI headers from v6.1... OK350350+ Installing user-facing UAPI headers from v6.0... OK351351+ Checking changes to UAPI headers between v6.0 and v6.1...352352+353353+ --- snip ---354354+ error - 37/907 UAPI headers compatible with x86 appear _not_ to be backwards compatible355355+356356+Note: Before v5.3, a header file needed by the script is not present,357357+so the script is unable to check changes before then.358358+359359+You'll notice that the script detected many UAPI changes that are not360360+backwards compatible. Knowing that kernel UAPIs are supposed to be stable361361+forever, this is an alarming result. This brings us to the next section:362362+caveats.363363+364364+Caveats365365+=======366366+367367+The UAPI checker makes no assumptions about the author's intention, so some368368+types of changes may be flagged even though they intentionally break UAPI.369369+370370+Removals For Refactoring or Deprecation371371+---------------------------------------372372+373373+Sometimes drivers for very old hardware are removed, such as in this example::374374+375375+ % ./scripts/check-uapi.sh -b ba47652ba655376376+ Installing user-facing UAPI headers from ba47652ba655... OK377377+ Installing user-facing UAPI headers from ba47652ba655^1... OK378378+ Checking changes to UAPI headers between ba47652ba655^1 and ba47652ba655...379379+ ==== UAPI header include/linux/meye.h was removed between ba47652ba655^1 and ba47652ba655 ====380380+381381+ error - 1/910 UAPI headers compatible with x86 appear _not_ to be backwards compatible382382+383383+The script will always flag removals (even if they're intentional).384384+385385+Struct Expansions386386+-----------------387387+388388+Depending on how a structure is handled in kernelspace, a change which389389+expands a struct could be non-breaking.390390+391391+If a struct is used as the argument to an ioctl, then the kernel driver392392+must be able to handle ioctl commands of any size. Beyond that, you need393393+to be careful when copying data from the user. Say, for example, that394394+``struct foo`` is changed like this::395395+396396+ struct foo {397397+ __u64 a; /* added in version 1 */398398+ + __u32 b; /* added in version 2 */399399+ + __u32 c; /* added in version 2 */400400+ }401401+402402+By default, the script will flag this kind of change for further review::403403+404404+ [C] 'struct foo' changed:405405+ type size changed from 64 to 128 (in bits)406406+ 2 data member insertions:407407+ '__u32 b', at offset 64 (in bits)408408+ '__u32 c', at offset 96 (in bits)409409+410410+However, it is possible that this change was made safely.411411+412412+If a userspace program was built with version 1, it will think413413+``sizeof(struct foo)`` is 8. That size will be encoded in the414414+ioctl value that gets sent to the kernel. If the kernel is built415415+with version 2, it will think the ``sizeof(struct foo)`` is 16.416416+417417+The kernel can use the ``_IOC_SIZE`` macro to get the size encoded418418+in the ioctl code that the user passed in and then use419419+``copy_struct_from_user()`` to safely copy the value::420420+421421+ int handle_ioctl(unsigned long cmd, unsigned long arg)422422+ {423423+ switch _IOC_NR(cmd) {424424+ 0x01: {425425+ struct foo my_cmd; /* size 16 in the kernel */426426+427427+ ret = copy_struct_from_user(&my_cmd, arg, sizeof(struct foo), _IOC_SIZE(cmd));428428+ ...429429+430430+``copy_struct_from_user`` will zero the struct in the kernel and then copy431431+only the bytes passed in from the user (leaving new members zeroized).432432+If the user passed in a larger struct, the extra members are ignored.433433+434434+If you know this situation is accounted for in the kernel code, you can435435+pass ``-i`` to the script, and struct expansions like this will be ignored.436436+437437+Flex Array Migration438438+--------------------439439+440440+While the script handles expansion into an existing flex array, it does441441+still flag initial migration to flex arrays from 1-element fake flex442442+arrays. For example::443443+444444+ struct foo {445445+ __u32 x;446446+ - __u32 flex[1]; /* fake flex */447447+ + __u32 flex[]; /* real flex */448448+ };449449+450450+This change would be flagged by the script::451451+452452+ [C] 'struct foo' changed:453453+ type size changed from 64 to 32 (in bits)454454+ 1 data member change:455455+ type of '__u32 flex[1]' changed:456456+ type name changed from '__u32[1]' to '__u32[]'457457+ array type size changed from 32 to 'unknown'458458+ array type subrange 1 changed length from 1 to 'unknown'459459+460460+At this time, there's no way to filter these types of changes, so be461461+aware of this possible false positive.462462+463463+Summary464464+-------465465+466466+While many types of false positives are filtered out by the script,467467+it's possible there are some cases where the script flags a change468468+which does not break UAPI. It's also possible a change which *does*469469+break userspace would not be flagged by this script. While the script470470+has been run on much of the kernel history, there could still be corner471471+cases that are not accounted for.472472+473473+The intention is for this script to be used as a quick check for474474+maintainers or automated tooling, not as the end-all authority on475475+patch compatibility. It's best to remember: use your best judgment476476+(and ideally a unit test in userspace) to make sure your UAPI changes477477+are backwards-compatible!