1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Module version support
4  *
5  * Copyright (C) 2008 Rusty Russell
6  */
7 
8 #include <linux/module.h>
9 #include <linux/string.h>
10 #include <linux/printk.h>
11 #include "internal.h"
12 
check_version(const struct load_info * info,const char * symname,struct module * mod,const u32 * crc)13 int check_version(const struct load_info *info,
14 		  const char *symname,
15 			 struct module *mod,
16 			 const u32 *crc)
17 {
18 	Elf_Shdr *sechdrs = info->sechdrs;
19 	unsigned int versindex = info->index.vers;
20 	unsigned int i, num_versions;
21 	struct modversion_info *versions;
22 	struct modversion_info_ext version_ext;
23 
24 	/* Exporting module didn't supply crcs?  OK, we're already tainted. */
25 	if (!crc)
26 		return 1;
27 
28 	/* If we have extended version info, rely on it */
29 	if (info->index.vers_ext_crc) {
30 		for_each_modversion_info_ext(version_ext, info) {
31 			if (strcmp(version_ext.name, symname) != 0)
32 				continue;
33 			if (*version_ext.crc == *crc)
34 				return 1;
35 			pr_debug("Found checksum %X vs module %X\n",
36 				 *crc, *version_ext.crc);
37 			goto bad_version;
38 		}
39 		pr_warn_once("%s: no extended symbol version for %s\n",
40 			     info->name, symname);
41 		return 1;
42 	}
43 
44 	/* No versions at all?  modprobe --force does this. */
45 	if (versindex == 0)
46 		return try_to_force_load(mod, symname) == 0;
47 
48 	versions = (void *)sechdrs[versindex].sh_addr;
49 	num_versions = sechdrs[versindex].sh_size
50 		/ sizeof(struct modversion_info);
51 
52 	for (i = 0; i < num_versions; i++) {
53 		u32 crcval;
54 
55 		if (strcmp(versions[i].name, symname) != 0)
56 			continue;
57 
58 		crcval = *crc;
59 		if (versions[i].crc == crcval)
60 			return 1;
61 		pr_debug("Found checksum %X vs module %lX\n",
62 			 crcval, versions[i].crc);
63 		goto bad_version;
64 	}
65 
66 	/* Broken toolchain. Warn once, then let it go.. */
67 	pr_warn_once("%s: no symbol version for %s\n", info->name, symname);
68 	return 1;
69 
70 bad_version:
71 	pr_warn("%s: disagrees about version of symbol %s\n", info->name, symname);
72 	return 0;
73 }
74 
check_modstruct_version(const struct load_info * info,struct module * mod)75 int check_modstruct_version(const struct load_info *info,
76 			    struct module *mod)
77 {
78 	struct find_symbol_arg fsa = {
79 		.name	= "module_layout",
80 		.gplok	= true,
81 	};
82 
83 	/*
84 	 * Since this should be found in kernel (which can't be removed), no
85 	 * locking is necessary -- use preempt_disable() to placate lockdep.
86 	 */
87 	preempt_disable();
88 	if (!find_symbol(&fsa)) {
89 		preempt_enable();
90 		BUG();
91 	}
92 	preempt_enable();
93 	return check_version(info, "module_layout", mod, fsa.crc);
94 }
95 
96 /* First part is kernel version, which we ignore if module has crcs. */
same_magic(const char * amagic,const char * bmagic,bool has_crcs)97 int same_magic(const char *amagic, const char *bmagic,
98 	       bool has_crcs)
99 {
100 	if (has_crcs) {
101 		amagic += strcspn(amagic, " ");
102 		bmagic += strcspn(bmagic, " ");
103 	}
104 	return strcmp(amagic, bmagic) == 0;
105 }
106 
modversion_ext_start(const struct load_info * info,struct modversion_info_ext * start)107 void modversion_ext_start(const struct load_info *info,
108 			  struct modversion_info_ext *start)
109 {
110 	unsigned int crc_idx = info->index.vers_ext_crc;
111 	unsigned int name_idx = info->index.vers_ext_name;
112 	Elf_Shdr *sechdrs = info->sechdrs;
113 
114 	/*
115 	 * Both of these fields are needed for this to be useful
116 	 * Any future fields should be initialized to NULL if absent.
117 	 */
118 	if (crc_idx == 0 || name_idx == 0) {
119 		start->remaining = 0;
120 		return;
121 	}
122 
123 	start->crc = (const u32 *)sechdrs[crc_idx].sh_addr;
124 	start->name = (const char *)sechdrs[name_idx].sh_addr;
125 	start->remaining = sechdrs[crc_idx].sh_size / sizeof(*start->crc);
126 }
127 
modversion_ext_advance(struct modversion_info_ext * vers)128 void modversion_ext_advance(struct modversion_info_ext *vers)
129 {
130 	vers->remaining--;
131 	vers->crc++;
132 	vers->name += strlen(vers->name) + 1;
133 }
134 
135 /*
136  * Generate the signature for all relevant module structures here.
137  * If these change, we don't want to try to parse the module.
138  */
module_layout(struct module * mod,struct modversion_info * ver,struct kernel_param * kp,struct kernel_symbol * ks,struct tracepoint * const * tp)139 void module_layout(struct module *mod,
140 		   struct modversion_info *ver,
141 		   struct kernel_param *kp,
142 		   struct kernel_symbol *ks,
143 		   struct tracepoint * const *tp)
144 {
145 }
146 EXPORT_SYMBOL(module_layout);
147