[RFC PATCH 00/10] scalable symbol flags with __kflagstab

Siddharth Nayyar posted 10 patches 1 month ago
Documentation/kbuild/modules.rst  |   6 +-
include/asm-generic/vmlinux.lds.h |  21 +++----
include/linux/export-internal.h   |  28 ++++++---
include/linux/module.h            |   4 +-
include/linux/module_symbol.h     |   6 ++
kernel/module/internal.h          |   5 +-
kernel/module/main.c              | 101 ++++++++++++++----------------
scripts/mod/modpost.c             |  27 ++++++--
scripts/module.lds.S              |   3 +-
9 files changed, 107 insertions(+), 94 deletions(-)
[RFC PATCH 00/10] scalable symbol flags with __kflagstab
Posted by Siddharth Nayyar 1 month ago
Hi everyone,

This patch series proposes a new, scalable mechanism to represent
boolean flags for exported kernel symbols.

Problem Statement:

The core architectural issue with kernel symbol flags is our reliance on
splitting the main symbol table, ksymtab. To handle a single boolean
property, such as GPL-only, all exported symbols are split across two
separate tables: __ksymtab and __ksymtab_gpl.

This design forces the module loader to perform a separate search on
each of these tables for every symbol it needs, for vmlinux and for all
previously loaded modules.

This approach is fundamentally not scalable. If we were to introduce a
second flag, we would need four distinct symbol tables. For n boolean
flags, this model requires an exponential growth to 2^n tables,
dramatically increasing complexity.

Another consequence of this fragmentation is degraded performance. For
example, a binary search on the symbol table of vmlinux, that would take
only 14 comparison steps (assuming ~2^14 or 16K symbols) in a unified
table, can require up to 26 steps when spread across two tables
(assuming both tables have ~2^13 symbols). This performance penalty
worsens as more flags are added.

Proposed Solution:

This series introduces a __kflagstab section to store symbol flags in a
dedicated data structure, similar to how CRCs are handled in the
__kcrctab.

The flags for a given symbol in __kflagstab will be located at the same
index as the symbol's entry in __ksymtab and its CRC in __kcrctab. This
design decouples the flags from the symbol table itself, allowing us to
maintain a single, sorted __ksymtab. As a result, the symbol search
remains an efficient, single lookup, regardless of the number of flags
we add in the future.

The motivation for this change comes from the Android kernel, which uses
an additional symbol flag to restrict the use of certain exported
symbols by unsigned modules, thereby enhancing kernel security. This
__kflagstab can be implemented as a bitmap to efficiently manage which
symbols are available for general use versus those restricted to signed
modules only.

Patch Series Overview:

* Patch 1-8: Introduce the __kflagstab, migrate the existing GPL-only
  flag to this new mechanism, and clean up the old __ksymtab_gpl
  infrastructure.
* Patch 9-10: Add a "symbol import protection" flag,
  which disallows unsigned modules from importing symbols marked with
  this flag.

This is an RFC, and I am seeking feedback on the overall approach and
implementation before moving forward.

Thanks,
Siddharth Nayyar

Siddharth Nayyar (10):
  define kernel symbol flags
  linker: add kflagstab section to vmlinux and modules
  modpost: create entries for kflagstab
  module loader: use kflagstab instead of *_gpl sections
  modpost: put all exported symbols in ksymtab section
  module loader: remove references of *_gpl sections
  linker: remove *_gpl sections from vmlinux and modules
  remove references to *_gpl sections in documentation
  modpost: add symbol import protection flag to kflagstab
  module loader: enforce symbol import protection

 Documentation/kbuild/modules.rst  |   6 +-
 include/asm-generic/vmlinux.lds.h |  21 +++----
 include/linux/export-internal.h   |  28 ++++++---
 include/linux/module.h            |   4 +-
 include/linux/module_symbol.h     |   6 ++
 kernel/module/internal.h          |   5 +-
 kernel/module/main.c              | 101 ++++++++++++++----------------
 scripts/mod/modpost.c             |  27 ++++++--
 scripts/module.lds.S              |   3 +-
 9 files changed, 107 insertions(+), 94 deletions(-)

-- 
2.51.0.338.gd7d06c2dae-goog
Re: [RFC PATCH 00/10] scalable symbol flags with __kflagstab
Posted by Petr Pavlu 1 month ago
On 8/29/25 12:54 PM, Siddharth Nayyar wrote:
> Hi everyone,
> 
> This patch series proposes a new, scalable mechanism to represent
> boolean flags for exported kernel symbols.
> 
> Problem Statement:
> 
> The core architectural issue with kernel symbol flags is our reliance on
> splitting the main symbol table, ksymtab. To handle a single boolean
> property, such as GPL-only, all exported symbols are split across two
> separate tables: __ksymtab and __ksymtab_gpl.
> 
> This design forces the module loader to perform a separate search on
> each of these tables for every symbol it needs, for vmlinux and for all
> previously loaded modules.
> 
> This approach is fundamentally not scalable. If we were to introduce a
> second flag, we would need four distinct symbol tables. For n boolean
> flags, this model requires an exponential growth to 2^n tables,
> dramatically increasing complexity.
> 
> Another consequence of this fragmentation is degraded performance. For
> example, a binary search on the symbol table of vmlinux, that would take
> only 14 comparison steps (assuming ~2^14 or 16K symbols) in a unified
> table, can require up to 26 steps when spread across two tables
> (assuming both tables have ~2^13 symbols). This performance penalty
> worsens as more flags are added.
> 
> Proposed Solution:
> 
> This series introduces a __kflagstab section to store symbol flags in a
> dedicated data structure, similar to how CRCs are handled in the
> __kcrctab.
> 
> The flags for a given symbol in __kflagstab will be located at the same
> index as the symbol's entry in __ksymtab and its CRC in __kcrctab. This
> design decouples the flags from the symbol table itself, allowing us to
> maintain a single, sorted __ksymtab. As a result, the symbol search
> remains an efficient, single lookup, regardless of the number of flags
> we add in the future.

Merging __ksymtab and __ksymtab_gpl into a single section looks ok to
me, and similarly for __kcrctab and __kcrtab_gpl. The __ksymtab_gpl
support originally comes from commit 3344ea3ad4 ("[PATCH] MODULE_LICENSE
and EXPORT_SYMBOL_GPL support") [1], where it was named __gpl_ksymtab.
The commit doesn't mention why the implementation opts for using
a separate section, but I suspect it was designed this way to reduce
memory/disk usage.

A question is whether the symbol flags should be stored in a new
__kflagstab section, instead of adding a flag member to the existing
__ksymtab. As far as I'm aware, no userspace tool (particularly kmod)
uses the __ksymtab data, so we are free to update its format.

Note that I believe that __kcrctab/__kcrtab_gpl is a separate section
because the CRC data is available only if CONFIG_MODVERSIONS=y.

Including the flags as part of __ksymtab would be obviously a simpler
schema. On the other hand, an entry in __ksymtab has in the worst case
a size of 24 bytes with an 8-byte alignment requirement. This means that
adding a flag to it would require additional 8 bytes per symbol.

> 
> The motivation for this change comes from the Android kernel, which uses
> an additional symbol flag to restrict the use of certain exported
> symbols by unsigned modules, thereby enhancing kernel security. This
> __kflagstab can be implemented as a bitmap to efficiently manage which
> symbols are available for general use versus those restricted to signed
> modules only.

I think it would be useful to explain in more detail how this protected
schema is used in practice and what problem it solves. Who is allowed to
provide these limited unsigned modules and if the concern is kernel
security, can't you enforce the use of only signed modules?

[1] https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git/commit/?id=3344ea3ad4b7c302c846a680dbaeedf96ed45c02

-- 
Thanks,
Petr
Re: [RFC PATCH 00/10] scalable symbol flags with __kflagstab
Posted by Sid Nayyar 4 weeks, 1 day ago
On Mon, Sep 1, 2025 at 1:27 PM Petr Pavlu <petr.pavlu@suse.com> wrote:
> Merging __ksymtab and __ksymtab_gpl into a single section looks ok to
> me, and similarly for __kcrctab and __kcrtab_gpl. The __ksymtab_gpl
> support originally comes from commit 3344ea3ad4 ("[PATCH] MODULE_LICENSE
> and EXPORT_SYMBOL_GPL support") [1], where it was named __gpl_ksymtab.
> The commit doesn't mention why the implementation opts for using
> a separate section, but I suspect it was designed this way to reduce
> memory/disk usage.
>
> A question is whether the symbol flags should be stored in a new
> __kflagstab section, instead of adding a flag member to the existing
> __ksymtab. As far as I'm aware, no userspace tool (particularly kmod)
> uses the __ksymtab data, so we are free to update its format.
>
> Note that I believe that __kcrctab/__kcrtab_gpl is a separate section
> because the CRC data is available only if CONFIG_MODVERSIONS=y.
>
> Including the flags as part of __ksymtab would be obviously a simpler
> schema. On the other hand, an entry in __ksymtab has in the worst case
> a size of 24 bytes with an 8-byte alignment requirement. This means that
> adding a flag to it would require additional 8 bytes per symbol.

Thanks for looking into the history of the _gpl split. We also noted
that there were up to five separate arrays at one point.

We explored three approaches to this problem: using the existing
__ksymtab, packing flags as bit-vectors, and the proposed
__kflagstab. We ruled out the bit-vector approach due to its
complexity, which would only save a few bits per symbol. The
__ksymtab approach, while the simplest, was too wasteful of space.
The __kflagstab seems like a good compromise, offering a slight
increase in complexity over the __ksymtab method but requiring only
one extra byte per symbol.

> >
> > The motivation for this change comes from the Android kernel, which uses
> > an additional symbol flag to restrict the use of certain exported
> > symbols by unsigned modules, thereby enhancing kernel security. This
> > __kflagstab can be implemented as a bitmap to efficiently manage which
> > symbols are available for general use versus those restricted to signed
> > modules only.
>
> I think it would be useful to explain in more detail how this protected
> schema is used in practice and what problem it solves. Who is allowed to
> provide these limited unsigned modules and if the concern is kernel
> security, can't you enforce the use of only signed modules?

The Android Common Kernel source is compiled into what we call
GKI (Generic Kernel Image), which consists of a kernel and a
number of modules. We maintain a stable interface (based on CRCs and
types) between the GKI components and vendor-specific modules
(compiled by device manufacturers, e.g., for hardware-specific
drivers) for the lifetime of a given GKI version.

This interface is intentionally restricted to the minimal set of
symbols required by the union of all vendor modules; our partners
declare their requirements in symbol lists. Any additions to these
lists are reviewed to ensure kernel internals are not overly exposed.
For example, we restrict drivers from having the ability to open and
read arbitrary files. This ABI boundary also allows us to evolve
internal kernel types that are not exposed to vendor modules, for
example, when a security fix requires a type to change.

The mechanism we use for this is CONFIG_TRIM_UNUSED_KSYMS and
CONFIG_UNUSED_KSYMS_WHITELIST. This results in a ksymtab
containing two kinds of exported symbols: those explicitly required
by vendors ("vendor-listed") and those only required by GKI modules
("GKI use only").

On top of this, we have implemented symbol import protection
(covered in patches 9/10 and 10/10). This feature prevents vendor
modules from using symbols that are not on the vendor-listed
whitelist. It is built on top of CONFIG_MODULE_SIG. GKI modules are
signed with a specific key, while vendor modules are unsigned and thus
treated as untrusted. This distinction allows signed GKI modules to
use any symbol in the ksymtab, while unsigned vendor modules can only
access the declared subset. This provides a significant layer of
defense and security against potentially exploitable vendor module
code.

Finally, we have implemented symbol export protection, which prevents
a vendor module from impersonating a GKI module by exporting any of
the same symbols. Note that this protection is currently specific to
the Android kernel and is not included in this patch series.

We have several years of experience with older implementations of
these protection mechanisms. The code in this series is a
considerably cleaner and simpler version compared to what has been
shipping in Android kernels since Android 14 (6.1 kernel).

I hope this clarifies the goals of the patch set. Let me know if you
have further questions.

--
Thanks,
Siddharth Nayyar
Re: [RFC PATCH 00/10] scalable symbol flags with __kflagstab
Posted by Petr Pavlu 3 weeks, 4 days ago
On 9/4/25 1:28 AM, Sid Nayyar wrote:
> On Mon, Sep 1, 2025 at 1:27 PM Petr Pavlu <petr.pavlu@suse.com> wrote:
>> Merging __ksymtab and __ksymtab_gpl into a single section looks ok to
>> me, and similarly for __kcrctab and __kcrtab_gpl. The __ksymtab_gpl
>> support originally comes from commit 3344ea3ad4 ("[PATCH] MODULE_LICENSE
>> and EXPORT_SYMBOL_GPL support") [1], where it was named __gpl_ksymtab.
>> The commit doesn't mention why the implementation opts for using
>> a separate section, but I suspect it was designed this way to reduce
>> memory/disk usage.
>>
>> A question is whether the symbol flags should be stored in a new
>> __kflagstab section, instead of adding a flag member to the existing
>> __ksymtab. As far as I'm aware, no userspace tool (particularly kmod)
>> uses the __ksymtab data, so we are free to update its format.
>>
>> Note that I believe that __kcrctab/__kcrtab_gpl is a separate section
>> because the CRC data is available only if CONFIG_MODVERSIONS=y.
>>
>> Including the flags as part of __ksymtab would be obviously a simpler
>> schema. On the other hand, an entry in __ksymtab has in the worst case
>> a size of 24 bytes with an 8-byte alignment requirement. This means that
>> adding a flag to it would require additional 8 bytes per symbol.
> 
> Thanks for looking into the history of the _gpl split. We also noted
> that there were up to five separate arrays at one point.
> 
> We explored three approaches to this problem: using the existing
> __ksymtab, packing flags as bit-vectors, and the proposed
> __kflagstab. We ruled out the bit-vector approach due to its
> complexity, which would only save a few bits per symbol. The
> __ksymtab approach, while the simplest, was too wasteful of space.
> The __kflagstab seems like a good compromise, offering a slight
> increase in complexity over the __ksymtab method but requiring only
> one extra byte per symbol.

This sounds reasonable to me. Do you have any numbers on hand that would
show the impact of extending __ksymtab?

> 
>>>
>>> The motivation for this change comes from the Android kernel, which uses
>>> an additional symbol flag to restrict the use of certain exported
>>> symbols by unsigned modules, thereby enhancing kernel security. This
>>> __kflagstab can be implemented as a bitmap to efficiently manage which
>>> symbols are available for general use versus those restricted to signed
>>> modules only.
>>
>> I think it would be useful to explain in more detail how this protected
>> schema is used in practice and what problem it solves. Who is allowed to
>> provide these limited unsigned modules and if the concern is kernel
>> security, can't you enforce the use of only signed modules?
> 
> The Android Common Kernel source is compiled into what we call
> GKI (Generic Kernel Image), which consists of a kernel and a
> number of modules. We maintain a stable interface (based on CRCs and
> types) between the GKI components and vendor-specific modules
> (compiled by device manufacturers, e.g., for hardware-specific
> drivers) for the lifetime of a given GKI version.
> 
> This interface is intentionally restricted to the minimal set of
> symbols required by the union of all vendor modules; our partners
> declare their requirements in symbol lists. Any additions to these
> lists are reviewed to ensure kernel internals are not overly exposed.
> For example, we restrict drivers from having the ability to open and
> read arbitrary files. This ABI boundary also allows us to evolve
> internal kernel types that are not exposed to vendor modules, for
> example, when a security fix requires a type to change.
> 
> The mechanism we use for this is CONFIG_TRIM_UNUSED_KSYMS and
> CONFIG_UNUSED_KSYMS_WHITELIST. This results in a ksymtab
> containing two kinds of exported symbols: those explicitly required
> by vendors ("vendor-listed") and those only required by GKI modules
> ("GKI use only").
> 
> On top of this, we have implemented symbol import protection
> (covered in patches 9/10 and 10/10). This feature prevents vendor
> modules from using symbols that are not on the vendor-listed
> whitelist. It is built on top of CONFIG_MODULE_SIG. GKI modules are
> signed with a specific key, while vendor modules are unsigned and thus
> treated as untrusted. This distinction allows signed GKI modules to
> use any symbol in the ksymtab, while unsigned vendor modules can only
> access the declared subset. This provides a significant layer of
> defense and security against potentially exploitable vendor module
> code.

If I understand correctly, this is similar to the recently introduced
EXPORT_SYMBOL_FOR_MODULES() macro, but with a coarser boundary.

I think that if the goal is to control the kABI scope and limit the use
of certain symbols only to GKI modules, then having the protection
depend on whether the module is signed is somewhat odd. It doesn't give
me much confidence if vendor modules are unsigned in the Android
ecosystem. I would expect that you want to improve this in the long
term.

It would then make more sense to me if the protection was determined by
whether the module is in-tree (the "intree" flag in modinfo) or,
alternatively, if it is signed by a built-in trusted key. I feel this
way the feature could be potentially useful for other distributions that
care about the kABI scope and have ecosystems where vendor modules are
properly signed with some key. However, I'm not sure if this would still
work in your case.

> 
> Finally, we have implemented symbol export protection, which prevents
> a vendor module from impersonating a GKI module by exporting any of
> the same symbols. Note that this protection is currently specific to
> the Android kernel and is not included in this patch series.
> 
> We have several years of experience with older implementations of
> these protection mechanisms. The code in this series is a
> considerably cleaner and simpler version compared to what has been
> shipping in Android kernels since Android 14 (6.1 kernel).

I agree. I'm not aware of any other distribution that would immediately
need this feature, so it is good if the implementation is kept
reasonably straightforward and doesn't overly complicate the module
loader code.

-- 
Thanks,
Petr
Re: [RFC PATCH 00/10] scalable symbol flags with __kflagstab
Posted by Sid Nayyar 2 weeks, 3 days ago
On Mon, Sep 8, 2025 at 11:09 AM Petr Pavlu <petr.pavlu@suse.com> wrote:
> This sounds reasonable to me. Do you have any numbers on hand that would
> show the impact of extending __ksymtab?

I did performance analysis for module loading. The kflagstab
optimizes symbol search, which accounts for less than 2% of the
average module load time. Therefore, this change does not translate
into any meaningful gains (or losses) in module loading performance.

On the binary size side, the on-disk size for vmlinux is somewhat
inflated due to extra entries in .symtab and .strtab. Since these
sections are not part of the final Image, the only increase in the
in-memory size of the kernel is for the kflagstab itself. This new
table occupies 1 byte for each symbol in the ksymtab.

> > The Android Common Kernel source is compiled into what we call
> > GKI (Generic Kernel Image), which consists of a kernel and a
> > number of modules. We maintain a stable interface (based on CRCs and
> > types) between the GKI components and vendor-specific modules
> > (compiled by device manufacturers, e.g., for hardware-specific
> > drivers) for the lifetime of a given GKI version.
> >
> > This interface is intentionally restricted to the minimal set of
> > symbols required by the union of all vendor modules; our partners
> > declare their requirements in symbol lists. Any additions to these
> > lists are reviewed to ensure kernel internals are not overly exposed.
> > For example, we restrict drivers from having the ability to open and
> > read arbitrary files. This ABI boundary also allows us to evolve
> > internal kernel types that are not exposed to vendor modules, for
> > example, when a security fix requires a type to change.
> >
> > The mechanism we use for this is CONFIG_TRIM_UNUSED_KSYMS and
> > CONFIG_UNUSED_KSYMS_WHITELIST. This results in a ksymtab
> > containing two kinds of exported symbols: those explicitly required
> > by vendors ("vendor-listed") and those only required by GKI modules
> > ("GKI use only").
> >
> > On top of this, we have implemented symbol import protection
> > (covered in patches 9/10 and 10/10). This feature prevents vendor
> > modules from using symbols that are not on the vendor-listed
> > whitelist. It is built on top of CONFIG_MODULE_SIG. GKI modules are
> > signed with a specific key, while vendor modules are unsigned and thus
> > treated as untrusted. This distinction allows signed GKI modules to
> > use any symbol in the ksymtab, while unsigned vendor modules can only
> > access the declared subset. This provides a significant layer of
> > defense and security against potentially exploitable vendor module
> > code.
>
> If I understand correctly, this is similar to the recently introduced
> EXPORT_SYMBOL_FOR_MODULES() macro, but with a coarser boundary.
>
> I think that if the goal is to control the kABI scope and limit the use
> of certain symbols only to GKI modules, then having the protection
> depend on whether the module is signed is somewhat odd. It doesn't give
> me much confidence if vendor modules are unsigned in the Android
> ecosystem. I would expect that you want to improve this in the long
> term.

GKI modules are the only modules built in the same Kbuild as the
kernel image, which Google builds and provides to partners. In
contrast, vendor modules are built and packaged entirely by partners.

Google signs GKI modules with ephemeral keys. Since partners do
not have these keys, vendor modules are treated as unsigned by
the kernel.

To ensure the authenticity of these unsigned modules, partners
package them into a separate image that becomes one of the boot
partitions. This entire image is signed, and its signature is
verified by the bootloader at boot time.

> It would then make more sense to me if the protection was determined by
> whether the module is in-tree (the "intree" flag in modinfo) or,
> alternatively, if it is signed by a built-in trusted key. I feel this
> way the feature could be potentially useful for other distributions that
> care about the kABI scope and have ecosystems where vendor modules are
> properly signed with some key. However, I'm not sure if this would still
> work in your case.

Partners can produce both in-tree and out-of-tree modules. We do not
trust either type regarding symbol exposure, as there is no way to know
exactly what sources were used. Furthermore, symbols exported via
EXPORT_SYMBOL_FOR_MODULES can be accessed by any vendor module that
mimics the GKI module name.

Therefore, neither the in-tree flag nor the EXPORT_SYMBOL_FOR_MODULES
mechanism provides a strong enough guarantee for the Android kernel to
identify GKI modules.

Only module signatures are sufficient to allow a module to access the
full set of exported symbols.  Unsigned vendor modules may only access
the symbol subset declared ahead of time by partners.

In case such symbol protection is not useful for the Linux community, I
am happy to keep this as an Android-specific feature.  However, I would
urge you to at least accept the kflagstab, as it allows us (and
potentially other Linux distributions) to easily introduce additional
flags for symbols. It is also a simplification/clean-up of the module
loader code.

--
Thanks,
Siddharth Nayyar
Re: [RFC PATCH 00/10] scalable symbol flags with __kflagstab
Posted by Petr Pavlu 1 week, 4 days ago
On 9/15/25 5:53 PM, Sid Nayyar wrote:
> On Mon, Sep 8, 2025 at 11:09 AM Petr Pavlu <petr.pavlu@suse.com> wrote:
>> This sounds reasonable to me. Do you have any numbers on hand that would
>> show the impact of extending __ksymtab?
> 
> I did performance analysis for module loading. The kflagstab
> optimizes symbol search, which accounts for less than 2% of the
> average module load time. Therefore, this change does not translate
> into any meaningful gains (or losses) in module loading performance.
> 
> On the binary size side, the on-disk size for vmlinux is somewhat
> inflated due to extra entries in .symtab and .strtab. Since these
> sections are not part of the final Image, the only increase in the
> in-memory size of the kernel is for the kflagstab itself. This new
> table occupies 1 byte for each symbol in the ksymtab.

This is useful information. However, I was specifically interested in
the impact of having the new flags field present as part of __ksymtab
(kernel_symbol), compared to keeping it in a separate section. Sorry for
not being clear.

I ran a small test to get a better understanding of the different sizes.
I used v6.17-rc6 together with the openSUSE x86_64 config [1], which is
fairly large. The resulting vmlinux.bin (no debuginfo) had an on-disk
size of 58 MiB, and included 5937 + 6589 (GPL-only) exported symbols.

The following table summarizes my measurements and calculations
regarding the sizes of all sections related to exported symbols:

                      |  HAVE_ARCH_PREL32_RELOCATIONS  | !HAVE_ARCH_PREL32_RELOCATIONS
 Section              | Base [B] | Ext. [B] | Sep. [B] | Base [B] | Ext. [B] | Sep. [B]
----------------------------------------------------------------------------------------
 __ksymtab            |    71244 |   200416 |   150312 |   142488 |   400832 |   300624
 __ksymtab_gpl        |    79068 |       NA |       NA |   158136 |       NA |       NA
 __kcrctab            |    23748 |    50104 |    50104 |    23748 |    50104 |    50104
 __kcrctab_gpl        |    26356 |       NA |       NA |    26356 |       NA |       NA
 __ksymtab_strings    |   253628 |   253628 |   253628 |   253628 |   253628 |   253628
 __kflagstab          |       NA |       NA |    12526 |       NA |       NA |    12526
----------------------------------------------------------------------------------------
 Total                |   454044 |   504148 |   466570 |   604356 |   704564 |   616882
 Increase to base [%] |       NA |     11.0 |      2.8 |       NA |     16.6 |      2.1

The column "HAVE_ARCH_PREL32_RELOCATIONS -> Base" contains the numbers
that I measured. The rest of the values are calculated. The "Ext."
column represents the variant of extending __ksymtab, and the "Sep."
column represents the variant of having a separate __kflagstab. With
HAVE_ARCH_PREL32_RELOCATIONS, each kernel_symbol is 12 B in size and is
extended to 16 B. With !HAVE_ARCH_PREL32_RELOCATIONS, it is 24 B,
extended to 32 B. Note that this does not include the metadata needed to
relocate __ksymtab*, which is freed after the initial processing.

The base export data in this case totals 0.43 MiB. About 50% is used for
storing the names of exported symbols.

Adding __kflagstab as a separate section has a negligible impact, as
expected. When extending __ksymtab (kernel_symbol) instead, the worst
case with !HAVE_ARCH_PREL32_RELOCATIONS increases the export data size
by 16.6%.

Based on the above, I think introducing __kflagstab makes senses, as the
added complexity is minimal, although I feel we could probably also get
away with extending kernel_symbol.

> 
>>> The Android Common Kernel source is compiled into what we call
>>> GKI (Generic Kernel Image), which consists of a kernel and a
>>> number of modules. We maintain a stable interface (based on CRCs and
>>> types) between the GKI components and vendor-specific modules
>>> (compiled by device manufacturers, e.g., for hardware-specific
>>> drivers) for the lifetime of a given GKI version.
>>>
>>> This interface is intentionally restricted to the minimal set of
>>> symbols required by the union of all vendor modules; our partners
>>> declare their requirements in symbol lists. Any additions to these
>>> lists are reviewed to ensure kernel internals are not overly exposed.
>>> For example, we restrict drivers from having the ability to open and
>>> read arbitrary files. This ABI boundary also allows us to evolve
>>> internal kernel types that are not exposed to vendor modules, for
>>> example, when a security fix requires a type to change.
>>>
>>> The mechanism we use for this is CONFIG_TRIM_UNUSED_KSYMS and
>>> CONFIG_UNUSED_KSYMS_WHITELIST. This results in a ksymtab
>>> containing two kinds of exported symbols: those explicitly required
>>> by vendors ("vendor-listed") and those only required by GKI modules
>>> ("GKI use only").
>>>
>>> On top of this, we have implemented symbol import protection
>>> (covered in patches 9/10 and 10/10). This feature prevents vendor
>>> modules from using symbols that are not on the vendor-listed
>>> whitelist. It is built on top of CONFIG_MODULE_SIG. GKI modules are
>>> signed with a specific key, while vendor modules are unsigned and thus
>>> treated as untrusted. This distinction allows signed GKI modules to
>>> use any symbol in the ksymtab, while unsigned vendor modules can only
>>> access the declared subset. This provides a significant layer of
>>> defense and security against potentially exploitable vendor module
>>> code.
>>
>> If I understand correctly, this is similar to the recently introduced
>> EXPORT_SYMBOL_FOR_MODULES() macro, but with a coarser boundary.
>>
>> I think that if the goal is to control the kABI scope and limit the use
>> of certain symbols only to GKI modules, then having the protection
>> depend on whether the module is signed is somewhat odd. It doesn't give
>> me much confidence if vendor modules are unsigned in the Android
>> ecosystem. I would expect that you want to improve this in the long
>> term.
> 
> GKI modules are the only modules built in the same Kbuild as the
> kernel image, which Google builds and provides to partners. In
> contrast, vendor modules are built and packaged entirely by partners.
> 
> Google signs GKI modules with ephemeral keys. Since partners do
> not have these keys, vendor modules are treated as unsigned by
> the kernel.
> 
> To ensure the authenticity of these unsigned modules, partners
> package them into a separate image that becomes one of the boot
> partitions. This entire image is signed, and its signature is
> verified by the bootloader at boot time.
> 
>> It would then make more sense to me if the protection was determined by
>> whether the module is in-tree (the "intree" flag in modinfo) or,
>> alternatively, if it is signed by a built-in trusted key. I feel this
>> way the feature could be potentially useful for other distributions that
>> care about the kABI scope and have ecosystems where vendor modules are
>> properly signed with some key. However, I'm not sure if this would still
>> work in your case.
> 
> Partners can produce both in-tree and out-of-tree modules. We do not
> trust either type regarding symbol exposure, as there is no way to know
> exactly what sources were used. Furthermore, symbols exported via
> EXPORT_SYMBOL_FOR_MODULES can be accessed by any vendor module that
> mimics the GKI module name.
> 
> Therefore, neither the in-tree flag nor the EXPORT_SYMBOL_FOR_MODULES
> mechanism provides a strong enough guarantee for the Android kernel to
> identify GKI modules.
> 
> Only module signatures are sufficient to allow a module to access the
> full set of exported symbols.  Unsigned vendor modules may only access
> the symbol subset declared ahead of time by partners.

This seems to answer why the in-tree flag is not sufficient for you.
However, I also suggested an alternative that the symbol protection
could be determined by whether the module is signed by a key from the
.builtin_trusted_keys keyring, as opposed to being signed by another key
reachable from the .secondary_trusted_keys keyring or being completely
unsigned.

Distributions can require that external modules be signed and allow
additional keys to be added as Machine Owner Keys, which can be made
reachable from .secondary_trusted_keys. Nonetheless, such distributions
might be still interested in limiting the number of symbols that such
external modules can use.

I think this option is worth considering, as it could potentially make
this symbol protection useful for other distributions as well.

> 
> In case such symbol protection is not useful for the Linux community, I
> am happy to keep this as an Android-specific feature.  However, I would
> urge you to at least accept the kflagstab, as it allows us (and
> potentially other Linux distributions) to easily introduce additional
> flags for symbols. It is also a simplification/clean-up of the module
> loader code.

I'm personally ok with adding the kflagstab support. I think it
introduces minimal complexity and, as you point out, simplifies certain
aspects. Additionally, if we add it, I believe that adding the proposed
symbol protection is simple enough to be included as well, at least from
my perspective.

[1] https://github.com/openSUSE/kernel-source/blob/307f149d9100a0e229eb94cbb997ae61187995c3/config/x86_64/default

-- 
Thanks,
Petr
Re: [RFC PATCH 00/10] scalable symbol flags with __kflagstab
Posted by Sid Nayyar 1 week ago
On Mon, Sep 22, 2025 at 12:41 PM Petr Pavlu <petr.pavlu@suse.com> wrote:
> This is useful information. However, I was specifically interested in
> the impact of having the new flags field present as part of __ksymtab
> (kernel_symbol), compared to keeping it in a separate section. Sorry for
> not being clear.
>
> I ran a small test to get a better understanding of the different sizes.
> I used v6.17-rc6 together with the openSUSE x86_64 config [1], which is
> fairly large. The resulting vmlinux.bin (no debuginfo) had an on-disk
> size of 58 MiB, and included 5937 + 6589 (GPL-only) exported symbols.
>
> The following table summarizes my measurements and calculations
> regarding the sizes of all sections related to exported symbols:
>
>                       |  HAVE_ARCH_PREL32_RELOCATIONS  | !HAVE_ARCH_PREL32_RELOCATIONS
>  Section              | Base [B] | Ext. [B] | Sep. [B] | Base [B] | Ext. [B] | Sep. [B]
> ----------------------------------------------------------------------------------------
>  __ksymtab            |    71244 |   200416 |   150312 |   142488 |   400832 |   300624
>  __ksymtab_gpl        |    79068 |       NA |       NA |   158136 |       NA |       NA
>  __kcrctab            |    23748 |    50104 |    50104 |    23748 |    50104 |    50104
>  __kcrctab_gpl        |    26356 |       NA |       NA |    26356 |       NA |       NA
>  __ksymtab_strings    |   253628 |   253628 |   253628 |   253628 |   253628 |   253628
>  __kflagstab          |       NA |       NA |    12526 |       NA |       NA |    12526
> ----------------------------------------------------------------------------------------
>  Total                |   454044 |   504148 |   466570 |   604356 |   704564 |   616882
>  Increase to base [%] |       NA |     11.0 |      2.8 |       NA |     16.6 |      2.1
>
> The column "HAVE_ARCH_PREL32_RELOCATIONS -> Base" contains the numbers
> that I measured. The rest of the values are calculated. The "Ext."
> column represents the variant of extending __ksymtab, and the "Sep."
> column represents the variant of having a separate __kflagstab. With
> HAVE_ARCH_PREL32_RELOCATIONS, each kernel_symbol is 12 B in size and is
> extended to 16 B. With !HAVE_ARCH_PREL32_RELOCATIONS, it is 24 B,
> extended to 32 B. Note that this does not include the metadata needed to
> relocate __ksymtab*, which is freed after the initial processing.
>
> The base export data in this case totals 0.43 MiB. About 50% is used for
> storing the names of exported symbols.
>
> Adding __kflagstab as a separate section has a negligible impact, as
> expected. When extending __ksymtab (kernel_symbol) instead, the worst
> case with !HAVE_ARCH_PREL32_RELOCATIONS increases the export data size
> by 16.6%.
>
> Based on the above, I think introducing __kflagstab makes senses, as the
> added complexity is minimal, although I feel we could probably also get
> away with extending kernel_symbol.

This investigation is very informative, thank you for sharing your
findings. I am in agreement with your conclusions.

> This seems to answer why the in-tree flag is not sufficient for you.
> However, I also suggested an alternative that the symbol protection
> could be determined by whether the module is signed by a key from the
> .builtin_trusted_keys keyring, as opposed to being signed by another key
> reachable from the .secondary_trusted_keys keyring or being completely
> unsigned.
>
> Distributions can require that external modules be signed and allow
> additional keys to be added as Machine Owner Keys, which can be made
> reachable from .secondary_trusted_keys. Nonetheless, such distributions
> might be still interested in limiting the number of symbols that such
> external modules can use.
>
> I think this option is worth considering, as it could potentially make
> this symbol protection useful for other distributions as well.

This sounds like a great solution to enhance trust and security,
apologies for missing this in the previous email. I will explore this
approach, but I would like to do it in a separate series.

> I'm personally ok with adding the kflagstab support. I think it
> introduces minimal complexity and, as you point out, simplifies certain
> aspects. Additionally, if we add it, I believe that adding the proposed
> symbol protection is simple enough to be included as well, at least from
> my perspective.

Since we are in agreement, I would like to seek code review for this
series. The code is ready for review from my side, but if you prefer I
can send out a non-RFC patch series for code review.

--
Thanks,
Siddharth Nayyar