forked from mirrors/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>
This commit is contained in:
		
							parent
							
								
									1f7f31bf72
								
							
						
					
					
						commit
						8c88bc5b48
					
				
					 2 changed files with 478 additions and 0 deletions
				
			
		
							
								
								
									
										477
									
								
								Documentation/dev-tools/checkuapi.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										477
									
								
								Documentation/dev-tools/checkuapi.rst
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,477 @@ | ||||||
|  | .. SPDX-License-Identifier: GPL-2.0-only | ||||||
|  | 
 | ||||||
|  | ============ | ||||||
|  | UAPI Checker | ||||||
|  | ============ | ||||||
|  | 
 | ||||||
|  | The UAPI checker (``scripts/check-uapi.sh``) is a shell script which | ||||||
|  | checks UAPI header files for userspace backwards-compatibility across | ||||||
|  | the git tree. | ||||||
|  | 
 | ||||||
|  | Options | ||||||
|  | ======= | ||||||
|  | 
 | ||||||
|  | This section will describe the options with which ``check-uapi.sh`` | ||||||
|  | can be run. | ||||||
|  | 
 | ||||||
|  | Usage:: | ||||||
|  | 
 | ||||||
|  |     check-uapi.sh [-b BASE_REF] [-p PAST_REF] [-j N] [-l ERROR_LOG] [-i] [-q] [-v] | ||||||
|  | 
 | ||||||
|  | Available options:: | ||||||
|  | 
 | ||||||
|  |     -b BASE_REF    Base git reference to use for comparison. If unspecified or empty, | ||||||
|  |                    will use any dirty changes in tree to UAPI files. If there are no | ||||||
|  |                    dirty changes, HEAD will be used. | ||||||
|  |     -p PAST_REF    Compare BASE_REF to PAST_REF (e.g. -p v6.1). If unspecified or empty, | ||||||
|  |                    will use BASE_REF^1. Must be an ancestor of BASE_REF. Only headers | ||||||
|  |                    that exist on PAST_REF will be checked for compatibility. | ||||||
|  |     -j JOBS        Number of checks to run in parallel (default: number of CPU cores). | ||||||
|  |     -l ERROR_LOG   Write error log to file (default: no error log is generated). | ||||||
|  |     -i             Ignore ambiguous changes that may or may not break UAPI compatibility. | ||||||
|  |     -q             Quiet operation. | ||||||
|  |     -v             Verbose operation (print more information about each header being checked). | ||||||
|  | 
 | ||||||
|  | Environmental args:: | ||||||
|  | 
 | ||||||
|  |     ABIDIFF  Custom path to abidiff binary | ||||||
|  |     CC       C compiler (default is "gcc") | ||||||
|  |     ARCH     Target architecture of C compiler (default is host arch) | ||||||
|  | 
 | ||||||
|  | Exit codes:: | ||||||
|  | 
 | ||||||
|  |     0) Success | ||||||
|  |     1) ABI difference detected | ||||||
|  |     2) Prerequisite not met | ||||||
|  | 
 | ||||||
|  | Examples | ||||||
|  | ======== | ||||||
|  | 
 | ||||||
|  | Basic Usage | ||||||
|  | ----------- | ||||||
|  | 
 | ||||||
|  | First, let's try making a change to a UAPI header file that obviously | ||||||
|  | won't break userspace:: | ||||||
|  | 
 | ||||||
|  |     cat << 'EOF' | patch -l -p1 | ||||||
|  |     --- a/include/uapi/linux/acct.h | ||||||
|  |     +++ b/include/uapi/linux/acct.h | ||||||
|  |     @@ -21,7 +21,9 @@ | ||||||
|  |      #include <asm/param.h> | ||||||
|  |      #include <asm/byteorder.h> | ||||||
|  | 
 | ||||||
|  |     -/* | ||||||
|  |     +#define FOO | ||||||
|  |     + | ||||||
|  |     +/* | ||||||
|  |       *  comp_t is a 16-bit "floating" point number with a 3-bit base 8 | ||||||
|  |       *  exponent and a 13-bit fraction. | ||||||
|  |       *  comp2_t is 24-bit with 5-bit base 2 exponent and 20 bit fraction | ||||||
|  |     diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h | ||||||
|  |     EOF | ||||||
|  | 
 | ||||||
|  | Now, let's use the script to validate:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh | ||||||
|  |     Installing user-facing UAPI headers from dirty tree... OK | ||||||
|  |     Installing user-facing UAPI headers from HEAD... OK | ||||||
|  |     Checking changes to UAPI headers between HEAD and dirty tree... | ||||||
|  |     All 912 UAPI headers compatible with x86 appear to be backwards compatible | ||||||
|  | 
 | ||||||
|  | Let's add another change that *might* break userspace:: | ||||||
|  | 
 | ||||||
|  |     cat << 'EOF' | patch -l -p1 | ||||||
|  |     --- a/include/uapi/linux/bpf.h | ||||||
|  |     +++ b/include/uapi/linux/bpf.h | ||||||
|  |     @@ -74,7 +74,7 @@ struct bpf_insn { | ||||||
|  |             __u8    dst_reg:4;      /* dest register */ | ||||||
|  |             __u8    src_reg:4;      /* source register */ | ||||||
|  |             __s16   off;            /* signed offset */ | ||||||
|  |     -       __s32   imm;            /* signed immediate constant */ | ||||||
|  |     +       __u32   imm;            /* unsigned immediate constant */ | ||||||
|  |      }; | ||||||
|  | 
 | ||||||
|  |      /* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */ | ||||||
|  |     EOF | ||||||
|  | 
 | ||||||
|  | The script will catch this:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh | ||||||
|  |     Installing user-facing UAPI headers from dirty tree... OK | ||||||
|  |     Installing user-facing UAPI headers from HEAD... OK | ||||||
|  |     Checking changes to UAPI headers between HEAD and dirty tree... | ||||||
|  |     ==== ABI differences detected in include/linux/bpf.h from HEAD -> dirty tree ==== | ||||||
|  |         [C] 'struct bpf_insn' changed: | ||||||
|  |           type size hasn't changed | ||||||
|  |           1 data member change: | ||||||
|  |             type of '__s32 imm' changed: | ||||||
|  |               typedef name changed from __s32 to __u32 at int-ll64.h:27:1 | ||||||
|  |               underlying type 'int' changed: | ||||||
|  |                 type name changed from 'int' to 'unsigned int' | ||||||
|  |                 type size hasn't changed | ||||||
|  |     ================================================================================== | ||||||
|  | 
 | ||||||
|  |     error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible | ||||||
|  | 
 | ||||||
|  | In this case, the script is reporting the type change because it could | ||||||
|  | break a userspace program that passes in a negative number. Now, let's | ||||||
|  | say you know that no userspace program could possibly be using a negative | ||||||
|  | value in ``imm``, so changing to an unsigned type there shouldn't hurt | ||||||
|  | anything. You can pass the ``-i`` flag to the script to ignore changes | ||||||
|  | in which the userspace backwards compatibility is ambiguous:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh -i | ||||||
|  |     Installing user-facing UAPI headers from dirty tree... OK | ||||||
|  |     Installing user-facing UAPI headers from HEAD... OK | ||||||
|  |     Checking changes to UAPI headers between HEAD and dirty tree... | ||||||
|  |     All 912 UAPI headers compatible with x86 appear to be backwards compatible | ||||||
|  | 
 | ||||||
|  | Now, let's make a similar change that *will* break userspace:: | ||||||
|  | 
 | ||||||
|  |     cat << 'EOF' | patch -l -p1 | ||||||
|  |     --- a/include/uapi/linux/bpf.h | ||||||
|  |     +++ b/include/uapi/linux/bpf.h | ||||||
|  |     @@ -71,8 +71,8 @@ enum { | ||||||
|  | 
 | ||||||
|  |      struct bpf_insn { | ||||||
|  |             __u8    code;           /* opcode */ | ||||||
|  |     -       __u8    dst_reg:4;      /* dest register */ | ||||||
|  |             __u8    src_reg:4;      /* source register */ | ||||||
|  |     +       __u8    dst_reg:4;      /* dest register */ | ||||||
|  |             __s16   off;            /* signed offset */ | ||||||
|  |             __s32   imm;            /* signed immediate constant */ | ||||||
|  |      }; | ||||||
|  |     EOF | ||||||
|  | 
 | ||||||
|  | Since we're re-ordering an existing struct member, there's no ambiguity, | ||||||
|  | and the script will report the breakage even if you pass ``-i``:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh -i | ||||||
|  |     Installing user-facing UAPI headers from dirty tree... OK | ||||||
|  |     Installing user-facing UAPI headers from HEAD... OK | ||||||
|  |     Checking changes to UAPI headers between HEAD and dirty tree... | ||||||
|  |     ==== ABI differences detected in include/linux/bpf.h from HEAD -> dirty tree ==== | ||||||
|  |         [C] 'struct bpf_insn' changed: | ||||||
|  |           type size hasn't changed | ||||||
|  |           2 data member changes: | ||||||
|  |             '__u8 dst_reg' offset changed from 8 to 12 (in bits) (by +4 bits) | ||||||
|  |             '__u8 src_reg' offset changed from 12 to 8 (in bits) (by -4 bits) | ||||||
|  |     ================================================================================== | ||||||
|  | 
 | ||||||
|  |     error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible | ||||||
|  | 
 | ||||||
|  | Let's commit the breaking change, then commit the innocuous change:: | ||||||
|  | 
 | ||||||
|  |     % git commit -m 'Breaking UAPI change' include/uapi/linux/bpf.h | ||||||
|  |     [detached HEAD f758e574663a] Breaking UAPI change | ||||||
|  |      1 file changed, 1 insertion(+), 1 deletion(-) | ||||||
|  |     % git commit -m 'Innocuous UAPI change' include/uapi/linux/acct.h | ||||||
|  |     [detached HEAD 2e87df769081] Innocuous UAPI change | ||||||
|  |      1 file changed, 3 insertions(+), 1 deletion(-) | ||||||
|  | 
 | ||||||
|  | Now, let's run the script again with no arguments:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh | ||||||
|  |     Installing user-facing UAPI headers from HEAD... OK | ||||||
|  |     Installing user-facing UAPI headers from HEAD^1... OK | ||||||
|  |     Checking changes to UAPI headers between HEAD^1 and HEAD... | ||||||
|  |     All 912 UAPI headers compatible with x86 appear to be backwards compatible | ||||||
|  | 
 | ||||||
|  | It doesn't catch any breaking change because, by default, it only | ||||||
|  | compares ``HEAD`` to ``HEAD^1``. The breaking change was committed on | ||||||
|  | ``HEAD~2``. If we wanted the search scope to go back further, we'd have to | ||||||
|  | use the ``-p`` option to pass a different past reference. In this case, | ||||||
|  | let's pass ``-p HEAD~2`` to the script so it checks UAPI changes between | ||||||
|  | ``HEAD~2`` and ``HEAD``:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh -p HEAD~2 | ||||||
|  |     Installing user-facing UAPI headers from HEAD... OK | ||||||
|  |     Installing user-facing UAPI headers from HEAD~2... OK | ||||||
|  |     Checking changes to UAPI headers between HEAD~2 and HEAD... | ||||||
|  |     ==== ABI differences detected in include/linux/bpf.h from HEAD~2 -> HEAD ==== | ||||||
|  |         [C] 'struct bpf_insn' changed: | ||||||
|  |           type size hasn't changed | ||||||
|  |           2 data member changes: | ||||||
|  |             '__u8 dst_reg' offset changed from 8 to 12 (in bits) (by +4 bits) | ||||||
|  |             '__u8 src_reg' offset changed from 12 to 8 (in bits) (by -4 bits) | ||||||
|  |     ============================================================================== | ||||||
|  | 
 | ||||||
|  |     error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible | ||||||
|  | 
 | ||||||
|  | Alternatively, we could have also run with ``-b HEAD~``. This would set the | ||||||
|  | base reference to ``HEAD~`` so then the script would compare it to ``HEAD~^1``. | ||||||
|  | 
 | ||||||
|  | Architecture-specific Headers | ||||||
|  | ----------------------------- | ||||||
|  | 
 | ||||||
|  | Consider this change:: | ||||||
|  | 
 | ||||||
|  |     cat << 'EOF' | patch -l -p1 | ||||||
|  |     --- a/arch/arm64/include/uapi/asm/sigcontext.h | ||||||
|  |     +++ b/arch/arm64/include/uapi/asm/sigcontext.h | ||||||
|  |     @@ -70,6 +70,7 @@ struct sigcontext { | ||||||
|  |      struct _aarch64_ctx { | ||||||
|  |             __u32 magic; | ||||||
|  |             __u32 size; | ||||||
|  |     +       __u32 new_var; | ||||||
|  |      }; | ||||||
|  | 
 | ||||||
|  |      #define FPSIMD_MAGIC   0x46508001 | ||||||
|  |     EOF | ||||||
|  | 
 | ||||||
|  | This is a change to an arm64-specific UAPI header file. In this example, I'm | ||||||
|  | running the script from an x86 machine with an x86 compiler, so, by default, | ||||||
|  | the script only checks x86-compatible UAPI header files:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh | ||||||
|  |     Installing user-facing UAPI headers from dirty tree... OK | ||||||
|  |     Installing user-facing UAPI headers from HEAD... OK | ||||||
|  |     No changes to UAPI headers were applied between HEAD and dirty tree | ||||||
|  | 
 | ||||||
|  | With an x86 compiler, we can't check header files in ``arch/arm64``, so the | ||||||
|  | script doesn't even try. | ||||||
|  | 
 | ||||||
|  | If we want to check the header file, we'll have to use an arm64 compiler and | ||||||
|  | set ``ARCH`` accordingly:: | ||||||
|  | 
 | ||||||
|  |     % CC=aarch64-linux-gnu-gcc ARCH=arm64 ./scripts/check-uapi.sh | ||||||
|  |     Installing user-facing UAPI headers from dirty tree... OK | ||||||
|  |     Installing user-facing UAPI headers from HEAD... OK | ||||||
|  |     Checking changes to UAPI headers between HEAD and dirty tree... | ||||||
|  |     ==== ABI differences detected in include/asm/sigcontext.h from HEAD -> dirty tree ==== | ||||||
|  |         [C] 'struct _aarch64_ctx' changed: | ||||||
|  |           type size changed from 64 to 96 (in bits) | ||||||
|  |           1 data member insertion: | ||||||
|  |             '__u32 new_var', at offset 64 (in bits) at sigcontext.h:73:1 | ||||||
|  |         -- snip -- | ||||||
|  |         [C] 'struct zt_context' changed: | ||||||
|  |           type size changed from 128 to 160 (in bits) | ||||||
|  |           2 data member changes (1 filtered): | ||||||
|  |             '__u16 nregs' offset changed from 64 to 96 (in bits) (by +32 bits) | ||||||
|  |             '__u16 __reserved[3]' offset changed from 80 to 112 (in bits) (by +32 bits) | ||||||
|  |     ======================================================================================= | ||||||
|  | 
 | ||||||
|  |     error - 1/884 UAPI headers compatible with arm64 appear _not_ to be backwards compatible | ||||||
|  | 
 | ||||||
|  | We can see with ``ARCH`` and ``CC`` set properly for the file, the ABI | ||||||
|  | change is reported properly. Also notice that the total number of UAPI | ||||||
|  | header files checked by the script changes. This is because the number | ||||||
|  | of headers installed for arm64 platforms is different than x86. | ||||||
|  | 
 | ||||||
|  | Cross-Dependency Breakages | ||||||
|  | -------------------------- | ||||||
|  | 
 | ||||||
|  | Consider this change:: | ||||||
|  | 
 | ||||||
|  |     cat << 'EOF' | patch -l -p1 | ||||||
|  |     --- a/include/uapi/linux/types.h | ||||||
|  |     +++ b/include/uapi/linux/types.h | ||||||
|  |     @@ -52,7 +52,7 @@ typedef __u32 __bitwise __wsum; | ||||||
|  |      #define __aligned_be64 __be64 __attribute__((aligned(8))) | ||||||
|  |      #define __aligned_le64 __le64 __attribute__((aligned(8))) | ||||||
|  | 
 | ||||||
|  |     -typedef unsigned __bitwise __poll_t; | ||||||
|  |     +typedef unsigned short __bitwise __poll_t; | ||||||
|  | 
 | ||||||
|  |      #endif /*  __ASSEMBLY__ */ | ||||||
|  |      #endif /* _UAPI_LINUX_TYPES_H */ | ||||||
|  |     EOF | ||||||
|  | 
 | ||||||
|  | Here, we're changing a ``typedef`` in ``types.h``. This doesn't break | ||||||
|  | a UAPI in ``types.h``, but other UAPIs in the tree may break due to | ||||||
|  | this change:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh | ||||||
|  |     Installing user-facing UAPI headers from dirty tree... OK | ||||||
|  |     Installing user-facing UAPI headers from HEAD... OK | ||||||
|  |     Checking changes to UAPI headers between HEAD and dirty tree... | ||||||
|  |     ==== ABI differences detected in include/linux/eventpoll.h from HEAD -> dirty tree ==== | ||||||
|  |         [C] 'struct epoll_event' changed: | ||||||
|  |           type size changed from 96 to 80 (in bits) | ||||||
|  |           2 data member changes: | ||||||
|  |             type of '__poll_t events' changed: | ||||||
|  |               underlying type 'unsigned int' changed: | ||||||
|  |                 type name changed from 'unsigned int' to 'unsigned short int' | ||||||
|  |                 type size changed from 32 to 16 (in bits) | ||||||
|  |             '__u64 data' offset changed from 32 to 16 (in bits) (by -16 bits) | ||||||
|  |     ======================================================================================== | ||||||
|  |     include/linux/eventpoll.h did not change between HEAD and dirty tree... | ||||||
|  |     It's possible a change to one of the headers it includes caused this error: | ||||||
|  |     #include <linux/fcntl.h> | ||||||
|  |     #include <linux/types.h> | ||||||
|  | 
 | ||||||
|  | Note that the script noticed the failing header file did not change, | ||||||
|  | so it assumes one of its includes must have caused the breakage. Indeed, | ||||||
|  | we can see ``linux/types.h`` is used from ``eventpoll.h``. | ||||||
|  | 
 | ||||||
|  | UAPI Header Removals | ||||||
|  | -------------------- | ||||||
|  | 
 | ||||||
|  | Consider this change:: | ||||||
|  | 
 | ||||||
|  |     cat << 'EOF' | patch -l -p1 | ||||||
|  |     diff --git a/include/uapi/asm-generic/Kbuild b/include/uapi/asm-generic/Kbuild | ||||||
|  |     index ebb180aac74e..a9c88b0a8b3b 100644 | ||||||
|  |     --- a/include/uapi/asm-generic/Kbuild | ||||||
|  |     +++ b/include/uapi/asm-generic/Kbuild | ||||||
|  |     @@ -31,6 +31,6 @@ mandatory-y += stat.h | ||||||
|  |      mandatory-y += statfs.h | ||||||
|  |      mandatory-y += swab.h | ||||||
|  |      mandatory-y += termbits.h | ||||||
|  |     -mandatory-y += termios.h | ||||||
|  |     +#mandatory-y += termios.h | ||||||
|  |      mandatory-y += types.h | ||||||
|  |      mandatory-y += unistd.h | ||||||
|  |     EOF | ||||||
|  | 
 | ||||||
|  | This script removes a UAPI header file from the install list. Let's run | ||||||
|  | the script:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh | ||||||
|  |     Installing user-facing UAPI headers from dirty tree... OK | ||||||
|  |     Installing user-facing UAPI headers from HEAD... OK | ||||||
|  |     Checking changes to UAPI headers between HEAD and dirty tree... | ||||||
|  |     ==== UAPI header include/asm/termios.h was removed between HEAD and dirty tree ==== | ||||||
|  | 
 | ||||||
|  |     error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible | ||||||
|  | 
 | ||||||
|  | Removing a UAPI header is considered a breaking change, and the script | ||||||
|  | will flag it as such. | ||||||
|  | 
 | ||||||
|  | Checking Historic UAPI Compatibility | ||||||
|  | ------------------------------------ | ||||||
|  | 
 | ||||||
|  | You can use the ``-b`` and ``-p`` options to examine different chunks of your | ||||||
|  | git tree. For example, to check all changed UAPI header files between tags | ||||||
|  | v6.0 and v6.1, you'd run:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh -b v6.1 -p v6.0 | ||||||
|  |     Installing user-facing UAPI headers from v6.1... OK | ||||||
|  |     Installing user-facing UAPI headers from v6.0... OK | ||||||
|  |     Checking changes to UAPI headers between v6.0 and v6.1... | ||||||
|  | 
 | ||||||
|  |     --- snip --- | ||||||
|  |     error - 37/907 UAPI headers compatible with x86 appear _not_ to be backwards compatible | ||||||
|  | 
 | ||||||
|  | Note: Before v5.3, a header file needed by the script is not present, | ||||||
|  | so the script is unable to check changes before then. | ||||||
|  | 
 | ||||||
|  | You'll notice that the script detected many UAPI changes that are not | ||||||
|  | backwards compatible. Knowing that kernel UAPIs are supposed to be stable | ||||||
|  | forever, this is an alarming result. This brings us to the next section: | ||||||
|  | caveats. | ||||||
|  | 
 | ||||||
|  | Caveats | ||||||
|  | ======= | ||||||
|  | 
 | ||||||
|  | The UAPI checker makes no assumptions about the author's intention, so some | ||||||
|  | types of changes may be flagged even though they intentionally break UAPI. | ||||||
|  | 
 | ||||||
|  | Removals For Refactoring or Deprecation | ||||||
|  | --------------------------------------- | ||||||
|  | 
 | ||||||
|  | Sometimes drivers for very old hardware are removed, such as in this example:: | ||||||
|  | 
 | ||||||
|  |     % ./scripts/check-uapi.sh -b ba47652ba655 | ||||||
|  |     Installing user-facing UAPI headers from ba47652ba655... OK | ||||||
|  |     Installing user-facing UAPI headers from ba47652ba655^1... OK | ||||||
|  |     Checking changes to UAPI headers between ba47652ba655^1 and ba47652ba655... | ||||||
|  |     ==== UAPI header include/linux/meye.h was removed between ba47652ba655^1 and ba47652ba655 ==== | ||||||
|  | 
 | ||||||
|  |     error - 1/910 UAPI headers compatible with x86 appear _not_ to be backwards compatible | ||||||
|  | 
 | ||||||
|  | The script will always flag removals (even if they're intentional). | ||||||
|  | 
 | ||||||
|  | Struct Expansions | ||||||
|  | ----------------- | ||||||
|  | 
 | ||||||
|  | Depending on how a structure is handled in kernelspace, a change which | ||||||
|  | expands a struct could be non-breaking. | ||||||
|  | 
 | ||||||
|  | If a struct is used as the argument to an ioctl, then the kernel driver | ||||||
|  | must be able to handle ioctl commands of any size. Beyond that, you need | ||||||
|  | to be careful when copying data from the user. Say, for example, that | ||||||
|  | ``struct foo`` is changed like this:: | ||||||
|  | 
 | ||||||
|  |     struct foo { | ||||||
|  |         __u64 a; /* added in version 1 */ | ||||||
|  |     +   __u32 b; /* added in version 2 */ | ||||||
|  |     +   __u32 c; /* added in version 2 */ | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | By default, the script will flag this kind of change for further review:: | ||||||
|  | 
 | ||||||
|  |     [C] 'struct foo' changed: | ||||||
|  |       type size changed from 64 to 128 (in bits) | ||||||
|  |       2 data member insertions: | ||||||
|  |         '__u32 b', at offset 64 (in bits) | ||||||
|  |         '__u32 c', at offset 96 (in bits) | ||||||
|  | 
 | ||||||
|  | However, it is possible that this change was made safely. | ||||||
|  | 
 | ||||||
|  | If a userspace program was built with version 1, it will think | ||||||
|  | ``sizeof(struct foo)`` is 8. That size will be encoded in the | ||||||
|  | ioctl value that gets sent to the kernel. If the kernel is built | ||||||
|  | with version 2, it will think the ``sizeof(struct foo)`` is 16. | ||||||
|  | 
 | ||||||
|  | The kernel can use the ``_IOC_SIZE`` macro to get the size encoded | ||||||
|  | in the ioctl code that the user passed in and then use | ||||||
|  | ``copy_struct_from_user()`` to safely copy the value:: | ||||||
|  | 
 | ||||||
|  |     int handle_ioctl(unsigned long cmd, unsigned long arg) | ||||||
|  |     { | ||||||
|  |         switch _IOC_NR(cmd) { | ||||||
|  |         0x01: { | ||||||
|  |             struct foo my_cmd;  /* size 16 in the kernel */ | ||||||
|  | 
 | ||||||
|  |             ret = copy_struct_from_user(&my_cmd, arg, sizeof(struct foo), _IOC_SIZE(cmd)); | ||||||
|  |             ... | ||||||
|  | 
 | ||||||
|  | ``copy_struct_from_user`` will zero the struct in the kernel and then copy | ||||||
|  | only the bytes passed in from the user (leaving new members zeroized). | ||||||
|  | If the user passed in a larger struct, the extra members are ignored. | ||||||
|  | 
 | ||||||
|  | If you know this situation is accounted for in the kernel code, you can | ||||||
|  | pass ``-i`` to the script, and struct expansions like this will be ignored. | ||||||
|  | 
 | ||||||
|  | Flex Array Migration | ||||||
|  | -------------------- | ||||||
|  | 
 | ||||||
|  | While the script handles expansion into an existing flex array, it does | ||||||
|  | still flag initial migration to flex arrays from 1-element fake flex | ||||||
|  | arrays. For example:: | ||||||
|  | 
 | ||||||
|  |     struct foo { | ||||||
|  |           __u32 x; | ||||||
|  |     -     __u32 flex[1]; /* fake flex */ | ||||||
|  |     +     __u32 flex[];  /* real flex */ | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | This change would be flagged by the script:: | ||||||
|  | 
 | ||||||
|  |     [C] 'struct foo' changed: | ||||||
|  |       type size changed from 64 to 32 (in bits) | ||||||
|  |       1 data member change: | ||||||
|  |         type of '__u32 flex[1]' changed: | ||||||
|  |           type name changed from '__u32[1]' to '__u32[]' | ||||||
|  |           array type size changed from 32 to 'unknown' | ||||||
|  |           array type subrange 1 changed length from 1 to 'unknown' | ||||||
|  | 
 | ||||||
|  | At this time, there's no way to filter these types of changes, so be | ||||||
|  | aware of this possible false positive. | ||||||
|  | 
 | ||||||
|  | Summary | ||||||
|  | ------- | ||||||
|  | 
 | ||||||
|  | While many types of false positives are filtered out by the script, | ||||||
|  | it's possible there are some cases where the script flags a change | ||||||
|  | which does not break UAPI. It's also possible a change which *does* | ||||||
|  | break userspace would not be flagged by this script. While the script | ||||||
|  | has been run on much of the kernel history, there could still be corner | ||||||
|  | cases that are not accounted for. | ||||||
|  | 
 | ||||||
|  | The intention is for this script to be used as a quick check for | ||||||
|  | maintainers or automated tooling, not as the end-all authority on | ||||||
|  | patch compatibility. It's best to remember: use your best judgment | ||||||
|  | (and ideally a unit test in userspace) to make sure your UAPI changes | ||||||
|  | are backwards-compatible! | ||||||
|  | @ -34,6 +34,7 @@ Documentation/dev-tools/testing-overview.rst | ||||||
|    kselftest |    kselftest | ||||||
|    kunit/index |    kunit/index | ||||||
|    ktap |    ktap | ||||||
|  |    checkuapi | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| .. only::  subproject and html | .. only::  subproject and html | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 John Moon
						John Moon