Skip to content

Commit 08bc010

Browse files
nbdd0121piso77
authored andcommitted
UBUNTU: SAUCE: modpost: support arbitrary symbol length in modversion
BugLink: https://bugs.launchpad.net/bugs/2007654 Currently modversion uses a fixed size array of size (64 - sizeof(long)) to store symbol names, thus placing a hard limit on length of symbols. Rust symbols (which encodes crate and module names) can be quite a bit longer. The length limit in kallsyms is increased to 512 for this reason. It's a waste of space to simply expand the fixed array size to 512 in modversion info entries. I therefore make it variably sized, with offset to the next entry indicated by the initial "next" field. In addition to supporting longer-than-56/60 byte symbols, this patch also reduce the size for short symbols by getting rid of excessive 0 paddings. There are still some zero paddings to ensure "next" and "crc" fields are properly aligned. This patch does have a tiny drawback that it makes ".mod.c" files generated a bit less easy to read, as code like "\x08\x00\x00\x00\x78\x56\x34\x12" "symbol\0\0" is generated as opposed to { 0x12345678, "symbol" }, because the structure is now variable-length. But hopefully nobody reads the generated file :) Link: b8a94bf ("kallsyms: increase maximum kernel symbol length to 512") Link: Rust-for-Linux#379 Signed-off-by: Gary Guo <[email protected]> (backported from https://lore.kernel.org/lkml/[email protected]/) [ fix build error on s390x: always use a variable with TO_NATIVE() ] [ add dummy NULL entry at the end of the modversion array ] Signed-off-by: Andrea Righi <[email protected]>
1 parent 8f59968 commit 08bc010

File tree

5 files changed

+48
-31
lines changed

5 files changed

+48
-31
lines changed

arch/powerpc/kernel/module_64.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,12 +347,13 @@ static unsigned long get_got_size(const Elf64_Ehdr *hdr,
347347
static void dedotify_versions(struct modversion_info *vers,
348348
unsigned long size)
349349
{
350-
struct modversion_info *end;
350+
struct modversion_info *end = (void *)vers + size;
351351

352-
for (end = (void *)vers + size; vers < end; vers++)
352+
for (; vers < end && vers->next; vers = (void *)vers + vers->next) {
353353
if (vers->name[0] == '.') {
354354
memmove(vers->name, vers->name+1, strlen(vers->name));
355355
}
356+
}
356357
}
357358

358359
/*

include/linux/module.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@
3535
#define MODULE_NAME_LEN MAX_PARAM_PREFIX_LEN
3636

3737
struct modversion_info {
38-
unsigned long crc;
39-
char name[MODULE_NAME_LEN];
40-
};
38+
/* Offset of the next modversion entry in relation to this one. */
39+
u32 next;
40+
u32 crc;
41+
char name[0];
42+
} __packed;
4143

4244
struct module;
4345
struct exception_table_entry;

kernel/module/version.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,30 @@ int check_version(const struct load_info *info,
1717
{
1818
Elf_Shdr *sechdrs = info->sechdrs;
1919
unsigned int versindex = info->index.vers;
20-
unsigned int i, num_versions;
21-
struct modversion_info *versions;
20+
struct modversion_info *versions, *end;
21+
u32 crcval;
2222

2323
/* Exporting module didn't supply crcs? OK, we're already tainted. */
2424
if (!crc)
2525
return 1;
26+
crcval = *crc;
2627

2728
/* No versions at all? modprobe --force does this. */
2829
if (versindex == 0)
2930
return try_to_force_load(mod, symname) == 0;
3031

3132
versions = (void *)sechdrs[versindex].sh_addr;
32-
num_versions = sechdrs[versindex].sh_size
33-
/ sizeof(struct modversion_info);
33+
end = (void *)versions + sechdrs[versindex].sh_size;
3434

35-
for (i = 0; i < num_versions; i++) {
36-
u32 crcval;
37-
38-
if (strcmp(versions[i].name, symname) != 0)
35+
for (; versions < end && versions->next;
36+
versions = (void *)versions + versions->next) {
37+
if (strcmp(versions->name, symname) != 0)
3938
continue;
4039

41-
crcval = *crc;
42-
if (versions[i].crc == crcval)
40+
if (versions->crc == crcval)
4341
return 1;
44-
pr_debug("Found checksum %X vs module %lX\n",
45-
crcval, versions[i].crc);
42+
pr_debug("Found checksum %X vs module %X\n",
43+
crcval, versions->crc);
4644
goto bad_version;
4745
}
4846

scripts/export_report.pl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,18 +116,19 @@ sub collectcfiles {
116116
while ( <$module> ) {
117117
chomp;
118118
if ($state == 0) {
119-
$state = 1 if ($_ =~ /static const struct modversion_info/);
119+
$state = 1 if ($_ =~ /static const char ____versions/);
120120
next;
121121
}
122122
if ($state == 1) {
123-
$state = 2 if ($_ =~ /__attribute__\(\(section\("__versions"\)\)\)/);
123+
$state = 2 if ($_ =~ /__used __section\("__versions"\)/);
124124
next;
125125
}
126126
if ($state == 2) {
127-
if ( $_ !~ /0x[0-9a-f]+,/ ) {
127+
if ( $_ !~ /\\0"/ ) {
128+
last if ($_ =~ /;/);
128129
next;
129130
}
130-
my $sym = (split /([,"])/,)[4];
131+
my $sym = (split /(["\\])/,)[2];
131132
my ($module, $value, $symbol, $gpl) = @{$SYMBOL{$sym}};
132133
$SYMBOL{ $sym } = [ $module, $value+1, $symbol, $gpl];
133134
push(@{$MODULE{$thismod}} , $sym);

scripts/mod/modpost.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,13 +1900,17 @@ static void add_exported_symbols(struct buffer *buf, struct module *mod)
19001900
static void add_versions(struct buffer *b, struct module *mod)
19011901
{
19021902
struct symbol *s;
1903+
unsigned int name_len;
1904+
unsigned int name_len_padded;
1905+
unsigned int tmp;
1906+
unsigned char *tmp_view = (unsigned char *)&tmp;
19031907

19041908
if (!modversions)
19051909
return;
19061910

19071911
buf_printf(b, "\n");
1908-
buf_printf(b, "static const struct modversion_info ____versions[]\n");
1909-
buf_printf(b, "__used __section(\"__versions\") = {\n");
1912+
buf_printf(b, "static const char ____versions[]\n");
1913+
buf_printf(b, "__used __section(\"__versions\") =\n");
19101914

19111915
list_for_each_entry(s, &mod->unresolved_symbols, list) {
19121916
if (!s->module)
@@ -1916,16 +1920,27 @@ static void add_versions(struct buffer *b, struct module *mod)
19161920
s->name, mod->name);
19171921
continue;
19181922
}
1919-
if (strlen(s->name) >= MODULE_NAME_LEN) {
1920-
error("too long symbol \"%s\" [%s.ko]\n",
1921-
s->name, mod->name);
1922-
break;
1923-
}
1924-
buf_printf(b, "\t{ %#8x, \"%s\" },\n",
1925-
s->crc, s->name);
1923+
name_len = strlen(s->name);
1924+
name_len_padded = (name_len + 1 + 3) & ~3;
1925+
1926+
/* Offset to next entry */
1927+
tmp = 8 + name_len_padded;
1928+
tmp = TO_NATIVE(tmp);
1929+
buf_printf(b, "\t\"\\x%02x\\x%02x\\x%02x\\x%02x",
1930+
tmp_view[0], tmp_view[1], tmp_view[2], tmp_view[3]);
1931+
1932+
tmp = TO_NATIVE(s->crc);
1933+
buf_printf(b, "\\x%02x\\x%02x\\x%02x\\x%02x\"\n",
1934+
tmp_view[0], tmp_view[1], tmp_view[2], tmp_view[3]);
1935+
1936+
buf_printf(b, "\t\"%s", s->name);
1937+
for (; name_len < name_len_padded; name_len++)
1938+
buf_printf(b, "\\0");
1939+
buf_printf(b, "\"\n");
19261940
}
19271941

1928-
buf_printf(b, "};\n");
1942+
/* Always end with a NULL entry */
1943+
buf_printf(b, "\t\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\";\n");
19291944
}
19301945

19311946
static void add_depends(struct buffer *b, struct module *mod)

0 commit comments

Comments
 (0)