What Is Kernel Section Mismatch

What is kernel section mismatch?

It means that a function that is in a section with a given lifetime references something that is in a section with a different lifetime.

When the kernel binary is linked, different parts of the code and data are split up into different sections. Some of these sections are kept loaded all the time, but some others are removed once they are no longer needed (things that are only required during boot for example can be freed once boot is done - this saves memory).

If a function that is in a long-lasting section refers to data in one of the discardable sections, there is a problem - it might try to access that data when it has already been released, leading to all kinds of runtime issues.

This is not a warning you'll fix yourself, unless you wrote that code or are very familiar with it. It gets fixed by correctly annotating the function (or the data it refers to) so that it goes into the right section. The right fix can only be determined with detailed knowledge of that part of the kernel.


For a list of these sections and annotations, refer to the include/linux/init.h header in your kernel source tree:

/* These macros are used to mark some functions or 
* initialized data (doesn't apply to uninitialized data)
* as `initialization' functions. The kernel can take this
* as hint that the function is used only during the initialization
* phase and free up used memory resources after
*
* Usage:
* For functions:
*
* You should add __init immediately before the function name, like:
*
* static void __init initme(int x, int y)
* {
* extern int z; z = x * y;
* }
*
* If the function has a prototype somewhere, you can also add
* __init between closing brace of the prototype and semicolon:
*
* extern int initialize_foobar_device(int, int, int) __init;
*
* For initialized data:
* You should insert __initdata between the variable name and equal
* sign followed by value, e.g.:
*
* static int init_variable __initdata = 0;
* static const char linux_logo[] __initconst = { 0x32, 0x36, ... };
*
* Don't forget to initialize data not at file scope, i.e. within a function,
* as gcc otherwise puts the data into the bss section and not into the init
* section.
*
* Also note, that this data cannot be "const".
*/

/* These are for everybody (although not all archs will actually
discard it in modules) */
#define __init __section(.init.text) __cold notrace
#define __initdata __section(.init.data)
#define __initconst __section(.init.rodata)
#define __exitdata __section(.exit.data)
#define __exit_call __used __section(.exitcall.exit)

Others follow, with more comments and explanations.

See also the help text for the CONFIG_DEBUG_SECTION_MISMATCH Kconfig symbol:

The section mismatch analysis checks if there are illegal

references from one section to another section.

Linux will during link or during runtime drop some sections

and any use of code/data previously in these sections will

most likely result in an oops.

In the code functions and variables are annotated with

__init, __devinit etc. (see full list in include/linux/init.h)

which results in the code/data being placed in specific sections.

The section mismatch analysis is always done after a full

kernel build but enabling this option will in addition

do the following:

  • Add the option -fno-inline-functions-called-once to gcc

    When inlining a function annotated __init in a non-init

    function we would lose the section information and thus

    the analysis would not catch the illegal reference.

    This option tells gcc to inline less but will also

    result in a larger kernel.
  • Run the section mismatch analysis for each module/built-in.o

    When we run the section mismatch analysis on vmlinux.o we

    lose valueble information about where the mismatch was

    introduced.

    Running the analysis for each module/built-in.o file

    will tell where the mismatch happens much closer to the

    source. The drawback is that we will report the same

    mismatch at least twice.
  • Enable verbose reporting from modpost to help solving

    the section mismatches reported.

How to fix linux kernel section mismatches?

The solution was inside mpm.c and mpm-of.c.

I changed static int msm_mpm_debug_mask = 1 with static int msm_mpm_debug_mask __initdata = 1inside both file.

More info on the issue here

Section mismatch in reference from a function to another function during Linux kernel build after adding #pragma GCC optimize(O0)

TL;DR: gic_smp_init() should be annotated with __init.


Update: this was fixed in kernel v5.8, here's the relevant commit.

Looks to me like you found a bug: gic_smp_init() is only called by __init gic_init_bases(), so it has no real reason to not be annotated with __init too. The function set_smp_cross_call() is annotated with __init, so callers should also be annotated with __init.

If you compile with optimizations (without the pragma) this inconsistency disappears because the compiler simply inlines the entire body of gic_smp_init() into gic_init_bases(): the call chain is then just gic_init_bases -> set_smp_cross_call and everything is fine since they are both annotated __init. However, when you disable optimizations (#pragma GCC optimize ("O0")) the compiler no longer inlines the call to gic_smp_init(), which stays on its own as an actual function, and the inconsistency reveals itself.

The authors of the module probably missed the __init annotation of set_smp_cross_call(), or the annotation was added later and nobody noticed it also needed to be "propagated" in that driver code.

There also seems to be an inconsistency between the annotations for set_smp_cross_call(): in the C files (/arch/{arm,arm64}/kernel/smp.c) it is annotated with __init, while in the header files (/arch/{arm,arm64}/include/asm/smp.h) it is not. The latter instances should probably be annotated too as the doc-comment for the __init macro suggests:

 * If the function has a prototype somewhere, you can also add
* __init between closing brace of the prototype and semicolon:
*
* extern int initialize_foobar_device(int, int, int) __init;

Linux kernel CONFIG_DEBUG_SECTION_MISMATCH make errors

This is just a warning. The kernel build systems did a sanity check and found out something that might be an error. The warning message says somewhere in kernel code there is code that might do inappropriate cross section access. Note that your kernel did build!

To understand what the warning means, consider the following example:

Some kernel code in the kernel text section might be trying to call a function marked with the __init data macro, which the linker puts in the kernel init section that gets de-allocated after boot or module loading.

This might be a run time error since if the code in the text section calls the code in the init section after the initialization code has finished, it is basically calling a stale pointer.

Having said that, that call may be perfectly fine - it is possible that the calls in the kernel text section has some good reason to know that it only calls the function in the init section when it is guaranteed to be there.

This, of course, is just an example. Similar other scenarios also exists.

The solution is to compile with CONFIG_DEBUG_SECTION_MISMATCH=y which will give you output of what function is trying to access which data or function and which section they belong to. You can then try to figure out if the build time warning is warranted and if so hopefully fix.

The init.h macros __ref and __refdata can be used to allow such init references without warnings. For example,

char * __init_refok bar(void) 
{
static int flag = 0;
static char* rval = NULL;
if(!flag) {
flag = 1;
rval = init_fn(); /* a function discarded after init */
}
return rval;
}

__init_refok, etc can fix "valid" instances, so the fact they exist may not inspire confidence.

significance of __cpuinit and modpost warnings related to it

__cpuinit actually tells the compiler to put the function into the specified elf section.

#define __cpuinit        __section(.cpuinit.text) __cold

the kernel code says in include/linux/init.h:

 /* modpost check for section mismatches during the kernel build.
* A section mismatch happens when there are references from a
* code or data section to an init section (both code or data).
* The init sections are (for most archs) discarded by the kernel
* when early init has completed so all such references are potential bugs.
* For exit sections the same issue exists. ......

Mismatch between manpage and kernel behavior about getsockname


Short answer

I believe that the addrlen value is not checked in kernel just to not waste some CPU cycles, because it should always be of known type (e.g. struct sockaddr), therefore it should always has known and fixed size (which is 16 bytes). So kernel just rewrites addrlen to 16, no matter what.

Regarding the issue you are having: I'm not sure why it's happening, but it doesn't actually seem that it's about size mismatch. I'm pretty sure kernel and userspace both have the same size of that structure which should be passed to getsockname() syscall (proof is below). So basically the situation you are describing here:

...that makes the kernel copy more data (sizeof(sin_addr)) at the designed address (&sa) than the size of the variable on the stack at that address

is not the case. I could only imagine how many application would fail if it was true.

Detailed explanation

Userspace side

In iperf sources you have next definition of sockaddr struct (/usr/include/bits/socket.h):

/* Structure describing a generic socket address.  */
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};

And __SOCKADDR_COMMON macro defined as follows (/usr/include/bits/sockaddr.h):

/* This macro is used to declare the initial common members
of the data types used for socket addresses, `struct sockaddr',
`struct sockaddr_in', `struct sockaddr_un', etc. */

#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family

And sa_family_t defined as:

/* POSIX.1g specifies this type name for the `sa_family' member.  */
typedef unsigned short int sa_family_t;

So basically sizeof(struct sockaddr) is always 16 bytes (= sizeof(char[14]) + sizeof(short)).

Kernel side

In inet_getname() function you see that addrlen param is rewritten by next value:

*uaddr_len = sizeof(*sin);

where sin is:

DECLARE_SOCKADDR(struct sockaddr_in *, sin, uaddr);

So you see that sin has type of struct sockaddr_in *. This structure is defined as follows (include/uapi/linux/in.h):

/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */

/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};

So sin variable is also 16 bytes long.

UPDATE

I'll try to reply to your comment:

if getsockname wants to allocate an ipv6 instead that may be why it overflows the buffer

When calling getsockname() for AF_INET6 socket, kernel will figure (in getsockname() syscall, by sockfd_lookup_light() function) that inet6_getname() should be called to handle your request. In that case, uaddr_len will be assigned with next value:

struct sockaddr_in6 *sin = (struct sockaddr_in6 *)uaddr;
...
*uaddr_len = sizeof(*sin);

So if you are using sockaddr_in6 struct in your user-space program too, the size will be the same. Of course, if your userspace application is passing sockaddr structure to getsockname for AF_INET6 socket, there will be some sort of overflow (because sizeof(struct sockaddr_in6) > sizeof(struct sockaddr)). But I believe it's not the case for iperf3 tool you are using. And if it is -- it's iperf that should be fixed in the first place, and not the kernel.



Related Topics



Leave a reply



Submit