1 | The current kernel behavior is IMA measurements snapshot is taken at | 1 | The current kernel behavior is IMA measurements snapshot is taken at |
---|---|---|---|
2 | kexec 'load' and not at kexec 'execute'. IMA log is then carried | 2 | kexec 'load' and not at kexec 'execute'. IMA log is then carried |
3 | over to the new kernel after kexec 'execute'. | 3 | over to the new kernel after kexec 'execute'. |
4 | 4 | ||
5 | New events can be measured during/after the IMA log snapshot at kexec | 5 | Currently, the kernel behavior during kexec load is to fetch the IMA |
6 | 'load' and before the system boots to the new kernel. In this scenario, | 6 | measurements log from TPM PCRs and store it in a buffer. When a kexec |
7 | the TPM PCRs are extended with these events, but they are not carried | 7 | reboot is triggered, this stored log buffer is carried over to the second |
8 | over to the new kernel after kexec soft reboot since the snapshot is | 8 | kernel. However, the time gap between kexec load and kexec reboot can be |
9 | already taken. This results in mismatch between TPM PCR quotes and the | 9 | very long. During this time window, new events extended into TPM PCRs miss |
10 | actual IMA measurements list after kexec soft reboot, which in turn | 10 | the chance to be carried over to the second kernel. This results in |
11 | results in remote attestation failure. | 11 | mismatch between TPM PCR quotes and the actual IMA measurements list after |
12 | kexec soft reboot, which in turn results in remote attestation failure. | ||
12 | 13 | ||
13 | To solve this problem - | 14 | To solve this problem - |
14 | - allocate the necessary buffer at kexec 'load' time, | 15 | - allocate the necessary buffer at kexec 'load' time, |
15 | - populate the buffer with the IMA measurements at kexec 'execute' time, | 16 | - populate the buffer with the IMA measurements at kexec 'execute' time, |
16 | - and measure two new IMA events 'kexec_load' and 'kexec_execute' as | 17 | - and measure two new IMA events 'kexec_load' and 'kexec_execute' as |
... | ... | ||
60 | 61 | ||
61 | V7 of this series is available here[9] for reference. | 62 | V7 of this series is available here[9] for reference. |
62 | 63 | ||
63 | V8 of this series is available here[10] for reference. | 64 | V8 of this series is available here[10] for reference. |
64 | 65 | ||
66 | V9 of this series is available here[11] for reference. | ||
67 | |||
68 | V10 of this series is available here[12] for reference. | ||
69 | |||
65 | References: | 70 | References: |
66 | ----------- | 71 | ----------- |
67 | 72 | ||
68 | [1] [PATHC v2 5/9] ima: on soft reboot, save the measurement list | 73 | [1] [PATHC v2 5/9] ima: on soft reboot, save the measurement list |
69 | https://lore.kernel.org/lkml/1472596811-9596-6-git-send-email-zohar@linux.vnet.ibm.com/ | 74 | https://lore.kernel.org/lkml/1472596811-9596-6-git-send-email-zohar@linux.vnet.ibm.com/ |
... | ... | ||
92 | [9] [PATCH v7 0/7] ima: kexec: measure events between kexec load and execute | 97 | [9] [PATCH v7 0/7] ima: kexec: measure events between kexec load and execute |
93 | https://lore.kernel.org/all/20250203232033.64123-1-chenste@linux.microsoft.com/ | 98 | https://lore.kernel.org/all/20250203232033.64123-1-chenste@linux.microsoft.com/ |
94 | 99 | ||
95 | [10] [PATCH v8 0/7] ima: kexec: measure events between kexec load and execute | 100 | [10] [PATCH v8 0/7] ima: kexec: measure events between kexec load and execute |
96 | https://lore.kernel.org/all/20250218225502.747963-1-chenste@linux.microsoft.com/ | 101 | https://lore.kernel.org/all/20250218225502.747963-1-chenste@linux.microsoft.com/ |
102 | |||
103 | [11] [PATCH v9 0/7] ima: kexec: measure events between kexec load and execute | ||
104 | https://lore.kernel.org/all/20250304190351.96975-1-chenste@linux.microsoft.com/ | ||
105 | |||
106 | [12] [PATCH v10 0/8] ima: kexec: measure events between kexec load and execute | ||
107 | https://lore.kernel.org/all/20250318010448.954-1-chenste@linux.microsoft.com/ | ||
108 | |||
109 | Change Log v11: | ||
110 | - Incorporated feedback from the community (Mimi Zohar and Baoquan He) on | ||
111 | v10 of this series[12]. | ||
112 | - [PATCH V10 2/8] was splited into two [PATCH V11 2/9] and [PATCH V11 7/9]. | ||
113 | Per Mimi comment on [PATCH V10 2/8]. | ||
114 | - Verified all the patches are bisect-safe by booting into each | ||
115 | patch and verifying multiple kexec 'load' operations work, | ||
116 | and also verifying kexec soft reboot works, and IMA log gets | ||
117 | carried over for each patch. | ||
118 | - Updated patch descriptions as necessary. | ||
119 | |||
120 | Change Log v10: | ||
121 | - Incorporated feedback from the community (Mimi Zohar, Baoquan He, and | ||
122 | kernel test robot) on v9 of this series[11]. | ||
123 | - [PATCH V9 1/7] was splited into two [PATCH V10 1/8] and [PATCH V10 2/8]. | ||
124 | Per Mimi comment on [PATCH V9 1/7]. | ||
125 | - Verified all the patches are bisect-safe by booting into each | ||
126 | patch and verifying multiple kexec 'load' operations work, | ||
127 | and also verifying kexec soft reboot works, and IMA log gets | ||
128 | carried over for each patch. | ||
97 | 129 | ||
98 | Change Log v9: | 130 | Change Log v9: |
99 | - Incorporated feedback from the community (Stefan Berger, Mimi Zohar, | 131 | - Incorporated feedback from the community (Stefan Berger, Mimi Zohar, |
100 | and kernel test robot) on v8 of this series[10]. | 132 | and kernel test robot) on v8 of this series[10]. |
101 | - Rebased the patch series to mainline 6.14.0-rc3. | 133 | - Rebased the patch series to mainline 6.14.0-rc3. |
... | ... | ||
215 | compilation. | 247 | compilation. |
216 | - Used virt_to_page instead of phys_to_page. | 248 | - Used virt_to_page instead of phys_to_page. |
217 | - Updated patch descriptions as necessary. | 249 | - Updated patch descriptions as necessary. |
218 | 250 | ||
219 | 251 | ||
220 | steven chen (7): | 252 | steven chen (9): |
221 | ima: copy only complete measurement records across kexec | 253 | ima: rename variable the set_file "file" to "ima_kexec_file" |
254 | ima: define and call ima_alloc_kexec_file_buf() | ||
222 | kexec: define functions to map and unmap segments | 255 | kexec: define functions to map and unmap segments |
223 | ima: kexec: skip IMA segment validation after kexec soft reboot | 256 | ima: kexec: skip IMA segment validation after kexec soft reboot |
224 | ima: kexec: define functions to copy IMA log at soft boot | 257 | ima: kexec: define functions to copy IMA log at soft boot |
225 | ima: kexec: move IMA log copy from kexec load to execute | 258 | ima: kexec: move IMA log copy from kexec load to execute |
259 | ima: verify if the segment size has changed | ||
226 | ima: make the kexec extra memory configurable | 260 | ima: make the kexec extra memory configurable |
227 | ima: measure kexec load and exec events as critical data | 261 | ima: measure kexec load and exec events as critical data |
228 | 262 | ||
229 | include/linux/ima.h | 3 + | 263 | include/linux/ima.h | 3 + |
230 | include/linux/kexec.h | 9 ++ | 264 | include/linux/kexec.h | 9 ++ |
231 | kernel/kexec_core.c | 54 ++++++++ | 265 | kernel/kexec_core.c | 54 ++++++++ |
232 | kernel/kexec_file.c | 32 +++++ | 266 | kernel/kexec_file.c | 33 ++++- |
233 | security/integrity/ima/Kconfig | 10 ++ | 267 | security/integrity/ima/Kconfig | 10 ++ |
234 | security/integrity/ima/ima.h | 7 + | 268 | security/integrity/ima/ima.h | 6 + |
235 | security/integrity/ima/ima_kexec.c | 210 ++++++++++++++++++++++++----- | 269 | security/integrity/ima/ima_kexec.c | 193 ++++++++++++++++++++++++----- |
236 | security/integrity/ima/ima_queue.c | 9 +- | 270 | security/integrity/ima/ima_queue.c | 5 + |
237 | 8 files changed, 297 insertions(+), 37 deletions(-) | 271 | 8 files changed, 279 insertions(+), 34 deletions(-) |
238 | 272 | ||
239 | -- | 273 | -- |
240 | 2.25.1 | 274 | 2.25.1 |
241 | 275 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | The current kernel behavior is IMA measurements snapshot is taken at | ||
2 | kexec 'load' and not at kexec 'execute'. IMA log is then carried | ||
3 | over to the new kernel after kexec 'execute'. However, the time gap | ||
4 | between kexec load and kexec reboot can be very long. During this | ||
5 | time window, new events extended into TPM PCRs miss the chance | ||
6 | to be carried over to the second kernel. | ||
7 | |||
8 | To address the above, the following approach is proposed: | ||
9 | - Allocate the necessary buffer during the kexec load phase. | ||
10 | - Populate this buffer with the IMA measurements during | ||
11 | the kexec execute phase. | ||
1 | 12 | ||
13 | In the current implementation, a local variable "file" of type seq_file | ||
14 | is used in the API ima_dump_measurement_list() to store the IMA measurements | ||
15 | to be carried over across kexec system call. To make this buffer accessible | ||
16 | at kexec 'execute' time, rename it to "ima_kexec_file" before making it | ||
17 | a file variable to better reflect its purpose. | ||
18 | |||
19 | Renaming the local variable "file" of type seq_file defined in the | ||
20 | ima_dump_measurement_list function to "ima_kexec_file" will improve code | ||
21 | readability and maintainability by making the variable's role more explicit. | ||
22 | |||
23 | Suggested-by: Mimi Zohar <zohar@linux.ibm.com> | ||
24 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | ||
25 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | ||
26 | --- | ||
27 | security/integrity/ima/ima_kexec.c | 31 +++++++++++++++--------------- | ||
28 | 1 file changed, 16 insertions(+), 15 deletions(-) | ||
29 | |||
30 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c | ||
31 | index XXXXXXX..XXXXXXX 100644 | ||
32 | --- a/security/integrity/ima/ima_kexec.c | ||
33 | +++ b/security/integrity/ima/ima_kexec.c | ||
34 | @@ -XXX,XX +XXX,XX @@ | ||
35 | static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | ||
36 | unsigned long segment_size) | ||
37 | { | ||
38 | + struct seq_file ima_kexec_file; | ||
39 | struct ima_queue_entry *qe; | ||
40 | - struct seq_file file; | ||
41 | struct ima_kexec_hdr khdr; | ||
42 | int ret = 0; | ||
43 | |||
44 | /* segment size can't change between kexec load and execute */ | ||
45 | - file.buf = vmalloc(segment_size); | ||
46 | - if (!file.buf) { | ||
47 | + ima_kexec_file.buf = vmalloc(segment_size); | ||
48 | + if (!ima_kexec_file.buf) { | ||
49 | ret = -ENOMEM; | ||
50 | goto out; | ||
51 | } | ||
52 | |||
53 | - file.file = NULL; | ||
54 | - file.size = segment_size; | ||
55 | - file.read_pos = 0; | ||
56 | - file.count = sizeof(khdr); /* reserved space */ | ||
57 | + ima_kexec_file.file = NULL; | ||
58 | + ima_kexec_file.size = segment_size; | ||
59 | + ima_kexec_file.read_pos = 0; | ||
60 | + ima_kexec_file.count = sizeof(khdr); /* reserved space */ | ||
61 | |||
62 | memset(&khdr, 0, sizeof(khdr)); | ||
63 | khdr.version = 1; | ||
64 | /* This is an append-only list, no need to hold the RCU read lock */ | ||
65 | list_for_each_entry_rcu(qe, &ima_measurements, later, true) { | ||
66 | - if (file.count < file.size) { | ||
67 | + if (ima_kexec_file.count < ima_kexec_file.size) { | ||
68 | khdr.count++; | ||
69 | - ima_measurements_show(&file, qe); | ||
70 | + ima_measurements_show(&ima_kexec_file, qe); | ||
71 | } else { | ||
72 | ret = -EINVAL; | ||
73 | break; | ||
74 | @@ -XXX,XX +XXX,XX @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | ||
75 | * fill in reserved space with some buffer details | ||
76 | * (eg. version, buffer size, number of measurements) | ||
77 | */ | ||
78 | - khdr.buffer_size = file.count; | ||
79 | + khdr.buffer_size = ima_kexec_file.count; | ||
80 | if (ima_canonical_fmt) { | ||
81 | khdr.version = cpu_to_le16(khdr.version); | ||
82 | khdr.count = cpu_to_le64(khdr.count); | ||
83 | khdr.buffer_size = cpu_to_le64(khdr.buffer_size); | ||
84 | } | ||
85 | - memcpy(file.buf, &khdr, sizeof(khdr)); | ||
86 | + memcpy(ima_kexec_file.buf, &khdr, sizeof(khdr)); | ||
87 | |||
88 | print_hex_dump_debug("ima dump: ", DUMP_PREFIX_NONE, 16, 1, | ||
89 | - file.buf, file.count < 100 ? file.count : 100, | ||
90 | + ima_kexec_file.buf, ima_kexec_file.count < 100 ? | ||
91 | + ima_kexec_file.count : 100, | ||
92 | true); | ||
93 | |||
94 | - *buffer_size = file.count; | ||
95 | - *buffer = file.buf; | ||
96 | + *buffer_size = ima_kexec_file.count; | ||
97 | + *buffer = ima_kexec_file.buf; | ||
98 | out: | ||
99 | if (ret == -EINVAL) | ||
100 | - vfree(file.buf); | ||
101 | + vfree(ima_kexec_file.buf); | ||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | -- | ||
106 | 2.25.1 | diff view generated by jsdifflib |
1 | In the current implementation, the ima_dump_measurement_list() API is | ||
---|---|---|---|
2 | called during the kexec "load" phase, where a buffer is allocated and | ||
3 | the measurement records are copied. Due to this, new events added after | ||
4 | kexec load but before kexec execute are not carried over to the new kernel | ||
5 | during kexec operation | ||
6 | |||
7 | To allow the buffer allocation and population to be separated into distinct | ||
8 | steps, make the function local seq_file "ima_kexec_file" to a file variable. | ||
9 | |||
1 | Carrying the IMA measurement list across kexec requires allocating a | 10 | Carrying the IMA measurement list across kexec requires allocating a |
2 | buffer and copying the measurement records. Separate allocating the | 11 | buffer and copying the measurement records. Separate allocating the |
3 | buffer and copying the measurement records into separate functions in | 12 | buffer and copying the measurement records into separate functions in |
4 | order to allocate the buffer at kexec 'load' and copy the measurements | 13 | order to allocate the buffer at kexec 'load' and copy the measurements |
5 | at kexec 'execute'. | 14 | at kexec 'execute'. |
6 | 15 | ||
7 | This patch includes the following changes: | ||
8 | - Refactor ima_dump_measurement_list() to move the memory allocation | ||
9 | to a separate function ima_alloc_kexec_file_buf() which allocates | ||
10 | buffer of size 'kexec_segment_size' at kexec 'load'. | ||
11 | - Make the local variable ima_kexec_file in ima_dump_measurement_list() | ||
12 | a local static to the file, so that it can be accessed from | ||
13 | ima_alloc_kexec_file_buf(). Compare actual memory required to ensure | ||
14 | there is enough memory for the entire measurement record. | ||
15 | - Copy only complete measurement records. | ||
16 | - Make necessary changes to the function ima_add_kexec_buffer() to call | ||
17 | the above two functions. | ||
18 | - Compared the memory size allocated with memory size of the entire | ||
19 | measurement record. Copy only complete measurement records if there | ||
20 | is enough memory. If there is not enough memory, it will not copy | ||
21 | any IMA measurement records, and this situation will result in a | ||
22 | failure of remote attestation. | ||
23 | |||
24 | Suggested-by: Mimi Zohar <zohar@linux.ibm.com> | ||
25 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 16 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
26 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 17 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
27 | --- | 18 | --- |
28 | security/integrity/ima/ima.h | 1 + | 19 | security/integrity/ima/ima_kexec.c | 46 +++++++++++++++++++++++------- |
29 | security/integrity/ima/ima_kexec.c | 105 ++++++++++++++++++++++------- | 20 | 1 file changed, 35 insertions(+), 11 deletions(-) |
30 | security/integrity/ima/ima_queue.c | 4 +- | ||
31 | 3 files changed, 83 insertions(+), 27 deletions(-) | ||
32 | 21 | ||
33 | diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h | ||
34 | index XXXXXXX..XXXXXXX 100644 | ||
35 | --- a/security/integrity/ima/ima.h | ||
36 | +++ b/security/integrity/ima/ima.h | ||
37 | @@ -XXX,XX +XXX,XX @@ bool ima_template_has_modsig(const struct ima_template_desc *ima_template); | ||
38 | int ima_restore_measurement_entry(struct ima_template_entry *entry); | ||
39 | int ima_restore_measurement_list(loff_t bufsize, void *buf); | ||
40 | int ima_measurements_show(struct seq_file *m, void *v); | ||
41 | +int ima_get_binary_runtime_entry_size(struct ima_template_entry *entry); | ||
42 | unsigned long ima_get_binary_runtime_size(void); | ||
43 | int ima_init_template(void); | ||
44 | void ima_init_template_list(void); | ||
45 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c | 22 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c |
46 | index XXXXXXX..XXXXXXX 100644 | 23 | index XXXXXXX..XXXXXXX 100644 |
47 | --- a/security/integrity/ima/ima_kexec.c | 24 | --- a/security/integrity/ima/ima_kexec.c |
48 | +++ b/security/integrity/ima/ima_kexec.c | 25 | +++ b/security/integrity/ima/ima_kexec.c |
49 | @@ -XXX,XX +XXX,XX @@ | 26 | @@ -XXX,XX +XXX,XX @@ |
50 | #include "ima.h" | 27 | #include "ima.h" |
51 | 28 | ||
52 | #ifdef CONFIG_IMA_KEXEC | 29 | #ifdef CONFIG_IMA_KEXEC |
53 | +static struct seq_file ima_kexec_file; | 30 | +static struct seq_file ima_kexec_file; |
54 | + | 31 | + |
55 | +static void ima_reset_kexec_file(struct seq_file *sf) | 32 | +static void ima_free_kexec_file_buf(struct seq_file *sf) |
56 | +{ | 33 | +{ |
34 | + vfree(sf->buf); | ||
57 | + sf->buf = NULL; | 35 | + sf->buf = NULL; |
58 | + sf->size = 0; | 36 | + sf->size = 0; |
59 | + sf->read_pos = 0; | 37 | + sf->read_pos = 0; |
60 | + sf->count = 0; | 38 | + sf->count = 0; |
61 | +} | 39 | +} |
62 | + | 40 | + |
63 | +static void ima_free_kexec_file_buf(struct seq_file *sf) | ||
64 | +{ | ||
65 | + vfree(sf->buf); | ||
66 | + ima_reset_kexec_file(sf); | ||
67 | +} | ||
68 | + | ||
69 | +static int ima_alloc_kexec_file_buf(size_t segment_size) | 41 | +static int ima_alloc_kexec_file_buf(size_t segment_size) |
70 | +{ | 42 | +{ |
71 | + /* | ||
72 | + * kexec 'load' may be called multiple times. | ||
73 | + * Free and realloc the buffer only if the segment_size is | ||
74 | + * changed from the previous kexec 'load' call. | ||
75 | + */ | ||
76 | + if (ima_kexec_file.buf && ima_kexec_file.size == segment_size) | ||
77 | + goto out; | ||
78 | + | ||
79 | + ima_free_kexec_file_buf(&ima_kexec_file); | 43 | + ima_free_kexec_file_buf(&ima_kexec_file); |
80 | + | 44 | + |
81 | + /* segment size can't change between kexec load and execute */ | 45 | + /* segment size can't change between kexec load and execute */ |
82 | + ima_kexec_file.buf = vmalloc(segment_size); | 46 | + ima_kexec_file.buf = vmalloc(segment_size); |
83 | + if (!ima_kexec_file.buf) | 47 | + if (!ima_kexec_file.buf) |
84 | + return -ENOMEM; | 48 | + return -ENOMEM; |
85 | + | 49 | + |
86 | + ima_kexec_file.size = segment_size; | 50 | + ima_kexec_file.size = segment_size; |
87 | + | ||
88 | +out: | ||
89 | + ima_kexec_file.read_pos = 0; | 51 | + ima_kexec_file.read_pos = 0; |
90 | + ima_kexec_file.count = sizeof(struct ima_kexec_hdr); /* reserved space */ | 52 | + ima_kexec_file.count = sizeof(struct ima_kexec_hdr); /* reserved space */ |
91 | + | 53 | + |
92 | + return 0; | 54 | + return 0; |
93 | +} | 55 | +} |
94 | + | 56 | + |
95 | +/* | ||
96 | + * Copy the measurement list to the allocated memory | ||
97 | + * compare the size of IMA measurement list with the size of the allocated memory | ||
98 | + * if the size of the allocated memory is not less than the size of IMA measurement list | ||
99 | + * copy the measurement list to the allocated memory. | ||
100 | + * else return error | ||
101 | + */ | ||
102 | static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | 57 | static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, |
103 | unsigned long segment_size) | 58 | unsigned long segment_size) |
104 | { | 59 | { |
60 | - struct seq_file ima_kexec_file; | ||
105 | struct ima_queue_entry *qe; | 61 | struct ima_queue_entry *qe; |
106 | - struct seq_file file; | ||
107 | struct ima_kexec_hdr khdr; | 62 | struct ima_kexec_hdr khdr; |
108 | int ret = 0; | 63 | int ret = 0; |
109 | + size_t entry_size = 0; | 64 | |
110 | 65 | /* segment size can't change between kexec load and execute */ | |
111 | - /* segment size can't change between kexec load and execute */ | 66 | - ima_kexec_file.buf = vmalloc(segment_size); |
112 | - file.buf = vmalloc(segment_size); | 67 | if (!ima_kexec_file.buf) { |
113 | - if (!file.buf) { | ||
114 | - ret = -ENOMEM; | 68 | - ret = -ENOMEM; |
115 | - goto out; | 69 | - goto out; |
116 | + if (!ima_kexec_file.buf) { | ||
117 | + pr_err("Kexec file buf not allocated\n"); | 70 | + pr_err("Kexec file buf not allocated\n"); |
118 | + return -EINVAL; | 71 | + return -EINVAL; |
119 | } | 72 | } |
120 | 73 | ||
121 | - file.file = NULL; | 74 | - ima_kexec_file.file = NULL; |
122 | - file.size = segment_size; | 75 | - ima_kexec_file.size = segment_size; |
123 | - file.read_pos = 0; | 76 | - ima_kexec_file.read_pos = 0; |
124 | - file.count = sizeof(khdr); /* reserved space */ | 77 | - ima_kexec_file.count = sizeof(khdr); /* reserved space */ |
125 | - | 78 | - |
126 | memset(&khdr, 0, sizeof(khdr)); | 79 | memset(&khdr, 0, sizeof(khdr)); |
127 | khdr.version = 1; | 80 | khdr.version = 1; |
128 | /* This is an append-only list, no need to hold the RCU read lock */ | 81 | /* This is an append-only list, no need to hold the RCU read lock */ |
129 | list_for_each_entry_rcu(qe, &ima_measurements, later, true) { | 82 | @@ -XXX,XX +XXX,XX @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, |
130 | - if (file.count < file.size) { | 83 | *buffer_size = ima_kexec_file.count; |
131 | + entry_size += ima_get_binary_runtime_entry_size(qe->entry); | 84 | *buffer = ima_kexec_file.buf; |
132 | + if (entry_size <= segment_size) { | ||
133 | khdr.count++; | ||
134 | - ima_measurements_show(&file, qe); | ||
135 | + ima_measurements_show(&ima_kexec_file, qe); | ||
136 | } else { | ||
137 | ret = -EINVAL; | ||
138 | + pr_err("IMA log file is too big for Kexec buf\n"); | ||
139 | break; | ||
140 | } | ||
141 | } | ||
142 | |||
143 | if (ret < 0) | ||
144 | goto out; | ||
145 | - | ||
146 | /* | ||
147 | * fill in reserved space with some buffer details | ||
148 | * (eg. version, buffer size, number of measurements) | ||
149 | */ | ||
150 | - khdr.buffer_size = file.count; | ||
151 | + khdr.buffer_size = ima_kexec_file.count; | ||
152 | if (ima_canonical_fmt) { | ||
153 | khdr.version = cpu_to_le16(khdr.version); | ||
154 | khdr.count = cpu_to_le64(khdr.count); | ||
155 | khdr.buffer_size = cpu_to_le64(khdr.buffer_size); | ||
156 | } | ||
157 | - memcpy(file.buf, &khdr, sizeof(khdr)); | ||
158 | + memcpy(ima_kexec_file.buf, &khdr, sizeof(khdr)); | ||
159 | |||
160 | print_hex_dump_debug("ima dump: ", DUMP_PREFIX_NONE, 16, 1, | ||
161 | - file.buf, file.count < 100 ? file.count : 100, | ||
162 | + ima_kexec_file.buf, ima_kexec_file.count < 100 ? | ||
163 | + ima_kexec_file.count : 100, | ||
164 | true); | ||
165 | |||
166 | - *buffer_size = file.count; | ||
167 | - *buffer = file.buf; | ||
168 | + *buffer_size = ima_kexec_file.count; | ||
169 | + *buffer = ima_kexec_file.buf; | ||
170 | + | ||
171 | out: | 85 | out: |
172 | - if (ret == -EINVAL) | 86 | - if (ret == -EINVAL) |
173 | - vfree(file.buf); | 87 | - vfree(ima_kexec_file.buf); |
174 | return ret; | 88 | return ret; |
175 | } | 89 | } |
176 | |||
177 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | ||
178 | |||
179 | /* use more understandable variable names than defined in kbuf */ | ||
180 | void *kexec_buffer = NULL; | ||
181 | - size_t kexec_buffer_size; | ||
182 | + size_t kexec_buffer_size = 0; | ||
183 | size_t kexec_segment_size; | ||
184 | int ret; | ||
185 | 90 | ||
186 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 91 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
187 | return; | 92 | return; |
188 | } | 93 | } |
189 | 94 | ||
190 | - ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, | ||
191 | - kexec_segment_size); | ||
192 | - if (!kexec_buffer) { | ||
193 | + ret = ima_alloc_kexec_file_buf(kexec_segment_size); | 95 | + ret = ima_alloc_kexec_file_buf(kexec_segment_size); |
194 | + if (ret < 0) { | 96 | + if (ret < 0) { |
195 | pr_err("Not enough memory for the kexec measurement buffer.\n"); | 97 | + pr_err("Not enough memory for the kexec measurement buffer.\n"); |
196 | return; | ||
197 | } | ||
198 | |||
199 | + ret = ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, | ||
200 | + kexec_segment_size); | ||
201 | + if (ret < 0) { | ||
202 | + pr_err("Failed to dump IMA measurements. Error:%d.\n", ret); | ||
203 | + return; | 98 | + return; |
204 | + } | 99 | + } |
205 | + | 100 | + |
206 | kbuf.buffer = kexec_buffer; | 101 | ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, |
207 | kbuf.bufsz = kexec_buffer_size; | 102 | kexec_segment_size); |
208 | kbuf.memsz = kexec_segment_size; | 103 | if (!kexec_buffer) { |
209 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | ||
210 | image->ima_buffer_size = kexec_segment_size; | ||
211 | image->ima_buffer = kexec_buffer; | ||
212 | |||
213 | + /* | ||
214 | + * kexec owns kexec_buffer after kexec_add_buffer() is called | ||
215 | + * and it will vfree() that buffer. | ||
216 | + */ | ||
217 | + ima_reset_kexec_file(&ima_kexec_file); | ||
218 | + | ||
219 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", | ||
220 | kbuf.mem); | ||
221 | } | ||
222 | diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c | ||
223 | index XXXXXXX..XXXXXXX 100644 | ||
224 | --- a/security/integrity/ima/ima_queue.c | ||
225 | +++ b/security/integrity/ima/ima_queue.c | ||
226 | @@ -XXX,XX +XXX,XX @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value, | ||
227 | * binary_runtime_measurement list entry, which contains a | ||
228 | * couple of variable length fields (e.g template name and data). | ||
229 | */ | ||
230 | -static int get_binary_runtime_size(struct ima_template_entry *entry) | ||
231 | +int ima_get_binary_runtime_entry_size(struct ima_template_entry *entry) | ||
232 | { | ||
233 | int size = 0; | ||
234 | |||
235 | @@ -XXX,XX +XXX,XX @@ static int ima_add_digest_entry(struct ima_template_entry *entry, | ||
236 | if (binary_runtime_size != ULONG_MAX) { | ||
237 | int size; | ||
238 | |||
239 | - size = get_binary_runtime_size(entry); | ||
240 | + size = ima_get_binary_runtime_entry_size(entry); | ||
241 | binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ? | ||
242 | binary_runtime_size + size : ULONG_MAX; | ||
243 | } | ||
244 | -- | 104 | -- |
245 | 2.25.1 | 105 | 2.25.1 | diff view generated by jsdifflib |
1 | The content of memory segments carried over to the new kernel during the | 1 | Currently, the kernel behavior during kexec load is to fetch the IMA |
---|---|---|---|
2 | kexec systemcall can be changed at kexec 'execute' stage, but the size of | 2 | measurements logs and store logs in kernel memory. When a kexec reboot is |
3 | the memory segments cannot be changed at kexec 'execute' stage. | 3 | triggered, these stored logs in the kernel memory are carried over to the |
4 | second kernel. However, the time gap between kexec load and kexec reboot | ||
5 | can be very long. During this time window, new events extended into TPM | ||
6 | PCRs miss the chance to be carried over to the second kernel. This results | ||
7 | in a mismatch between TPM PCR quotes and the actual IMA measurements list | ||
8 | after kexec reboot, leading to remote attestation failure. | ||
4 | 9 | ||
5 | To copy IMA measurement logs during the kexec operation, IMA needs to | 10 | To solve this problem, the new design defers reading the IMA measurements |
6 | allocate memory at the kexec 'load' stage and map the segments to the | 11 | logs into the kexec buffer to the kexec reboot phase, while still allocating |
7 | kimage structure. The mapped address will then be used to copy IMA | 12 | the necessary buffer at kexec load time because it is not appropriate to |
8 | measurements during the kexec 'execute' stage. | 13 | allocate memory at the kexec reboot moment. |
9 | 14 | ||
10 | Currently, the mechanism to map and unmap segments to the kimage | 15 | The content of memory segments carried over to the new kernel during the |
11 | structure is not available to subsystems outside of kexec. | 16 | kexec system call can be changed at the kexec 'execute' stage, but the size |
17 | of the memory segments cannot be changed at the kexec 'execute' stage. | ||
12 | 18 | ||
13 | Implement kimage_map_segment() to enable IMA to map measurement log list to | 19 | To copy IMA measurement logs during the kexec operation, IMA allocates |
14 | the kimage structure during kexec 'load' stage. This function takes a kimage | 20 | memory at the kexec 'load' stage and map the segments to the kimage |
15 | pointer, a memory address, and a size, then gathers the | 21 | structure. The mapped address will then be used to copy IMA measurements |
22 | during the kexec 'execute' stage. | ||
23 | |||
24 | Currently, the mechanism to map and unmap segments to the kimage structure | ||
25 | is not available to subsystems outside of kexec. | ||
26 | |||
27 | Implement kimage_map_segment() to enable IMA to map the measurement log | ||
28 | list to the kimage structure during the kexec 'load' stage. This function | ||
29 | takes a kimage pointer, a memory address, and a size, then gathers the | ||
16 | source pages within the specified address range, creates an array of page | 30 | source pages within the specified address range, creates an array of page |
17 | pointers, and maps these to a contiguous virtual address range. The | 31 | pointers, and maps these to a contiguous virtual address range. The |
18 | function returns the start virtual address of this range if successful, or NULL on | 32 | function returns the start virtual address of this range if successful, |
19 | failure. | 33 | or NULL on failure. |
20 | 34 | ||
21 | Implement kimage_unmap_segment() for unmapping segments | 35 | Implement kimage_unmap_segment() for unmapping segments using vunmap(). |
22 | using vunmap(). | ||
23 | 36 | ||
24 | From: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 37 | From: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
25 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 38 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
26 | Cc: Eric Biederman <ebiederm@xmission.com> | 39 | Cc: Eric Biederman <ebiederm@xmission.com> |
27 | Cc: Baoquan He <bhe@redhat.com> | 40 | Cc: Baoquan He <bhe@redhat.com> |
28 | Cc: Vivek Goyal <vgoyal@redhat.com> | 41 | Cc: Vivek Goyal <vgoyal@redhat.com> |
29 | Cc: Dave Young <dyoung@redhat.com> | 42 | Cc: Dave Young <dyoung@redhat.com> |
30 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 43 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
44 | Acked-by: Baoquan He <bhe@redhat.com> | ||
31 | --- | 45 | --- |
32 | include/linux/kexec.h | 6 +++++ | 46 | include/linux/kexec.h | 6 +++++ |
33 | kernel/kexec_core.c | 54 +++++++++++++++++++++++++++++++++++++++++++ | 47 | kernel/kexec_core.c | 54 +++++++++++++++++++++++++++++++++++++++++++ |
34 | 2 files changed, 60 insertions(+) | 48 | 2 files changed, 60 insertions(+) |
35 | 49 | ||
... | ... | ||
66 | } | 80 | } |
67 | 81 | ||
68 | +void *kimage_map_segment(struct kimage *image, | 82 | +void *kimage_map_segment(struct kimage *image, |
69 | + unsigned long addr, unsigned long size) | 83 | + unsigned long addr, unsigned long size) |
70 | +{ | 84 | +{ |
85 | + unsigned long src_page_addr, dest_page_addr = 0; | ||
71 | + unsigned long eaddr = addr + size; | 86 | + unsigned long eaddr = addr + size; |
72 | + unsigned long src_page_addr, dest_page_addr; | 87 | + kimage_entry_t *ptr, entry; |
88 | + struct page **src_pages; | ||
73 | + unsigned int npages; | 89 | + unsigned int npages; |
74 | + struct page **src_pages; | 90 | + void *vaddr = NULL; |
75 | + int i; | 91 | + int i; |
76 | + kimage_entry_t *ptr, entry; | ||
77 | + void *vaddr = NULL; | ||
78 | + | 92 | + |
79 | + /* | 93 | + /* |
80 | + * Collect the source pages and map them in a contiguous VA range. | 94 | + * Collect the source pages and map them in a contiguous VA range. |
81 | + */ | 95 | + */ |
82 | + npages = PFN_UP(eaddr) - PFN_DOWN(addr); | 96 | + npages = PFN_UP(eaddr) - PFN_DOWN(addr); |
... | ... | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
11 | kexec systemcall can be changed at kexec 'execute' stage, but the size | 11 | kexec systemcall can be changed at kexec 'execute' stage, but the size |
12 | and the location of the memory segments cannot be changed at kexec | 12 | and the location of the memory segments cannot be changed at kexec |
13 | 'execute' stage. | 13 | 'execute' stage. |
14 | 14 | ||
15 | However, during the kexec execute stage, if kexec_calculate_store_digests() | 15 | However, during the kexec execute stage, if kexec_calculate_store_digests() |
16 | API is call to update the digest, it does not reuse the same memory segment | 16 | API is called to update the digest, it does not reuse the same memory |
17 | allocated during the kexec 'load' stage and the new memory segment required | 17 | segment allocated during the kexec 'load' stage and the new memory segment |
18 | cannot be transferred/mapped to the new kernel. | 18 | required cannot be transferred/mapped to the new kernel. |
19 | 19 | ||
20 | As a result, digest verification will fail in verify_sha256_digest() | 20 | As a result, digest verification will fail in verify_sha256_digest() |
21 | after a kexec soft reboot into the new kernel. Therefore, the digest | 21 | after a kexec soft reboot into the new kernel. Therefore, the digest |
22 | calculation/verification of the IMA segment needs to be skipped. | 22 | calculation/verification of the IMA segment needs to be skipped. |
23 | 23 | ||
... | ... | ||
37 | Cc: Vivek Goyal <vgoyal@redhat.com> | 37 | Cc: Vivek Goyal <vgoyal@redhat.com> |
38 | Cc: Dave Young <dyoung@redhat.com> | 38 | Cc: Dave Young <dyoung@redhat.com> |
39 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 39 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
40 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | 40 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> |
41 | Reviewed-by: Mimi Zohar <zohar@linux.ibm.com> | 41 | Reviewed-by: Mimi Zohar <zohar@linux.ibm.com> |
42 | Acked-by: Baoquan He <bhe@redhat.com> | ||
42 | --- | 43 | --- |
43 | include/linux/kexec.h | 3 +++ | 44 | include/linux/kexec.h | 3 +++ |
44 | kernel/kexec_file.c | 22 ++++++++++++++++++++++ | 45 | kernel/kexec_file.c | 22 ++++++++++++++++++++++ |
45 | security/integrity/ima/ima_kexec.c | 3 +++ | 46 | security/integrity/ima/ima_kexec.c | 3 +++ |
46 | 3 files changed, 28 insertions(+) | 47 | 3 files changed, 28 insertions(+) |
... | ... | ||
116 | image->ima_buffer_size = kexec_segment_size; | 117 | image->ima_buffer_size = kexec_segment_size; |
117 | image->ima_buffer = kexec_buffer; | 118 | image->ima_buffer = kexec_buffer; |
118 | + image->ima_segment_index = image->nr_segments - 1; | 119 | + image->ima_segment_index = image->nr_segments - 1; |
119 | + image->is_ima_segment_index_set = true; | 120 | + image->is_ima_segment_index_set = true; |
120 | 121 | ||
121 | /* | 122 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", |
122 | * kexec owns kexec_buffer after kexec_add_buffer() is called | 123 | kbuf.mem); |
123 | -- | 124 | -- |
124 | 2.25.1 | 125 | 2.25.1 | diff view generated by jsdifflib |
1 | The IMA log is currently copied to the new kernel during kexec 'load' | 1 | The IMA log is currently copied to the new kernel during kexec 'load' |
---|---|---|---|
2 | using ima_dump_measurement_list(). However, the log copied at kexec | 2 | using ima_dump_measurement_list(). However, the log copied at kexec |
3 | 'load' may result in loss of IMA measurements that only occurred after | 3 | 'load' may result in loss of IMA measurements that only occurred after |
4 | kexec "load'. Therefore, the log needs to be copied during kexec | 4 | kexec "load'. Therefore, the log needs to be copied during kexec |
5 | 'execute'. Setup the needed infrastructure to move the IMA log copy from | 5 | 'execute'. Setup the needed infrastructure to move the IMA log copy from |
6 | kexec 'load' to 'execute'. | 6 | kexec 'load' to 'execute'. |
7 | 7 | ||
... | ... | ||
11 | Implement ima_kexec_post_load() function to be invoked after the new | 11 | Implement ima_kexec_post_load() function to be invoked after the new |
12 | Kernel image has been loaded for kexec. ima_kexec_post_load() maps the | 12 | Kernel image has been loaded for kexec. ima_kexec_post_load() maps the |
13 | IMA buffer to a segment in the newly loaded Kernel. It also registers | 13 | IMA buffer to a segment in the newly loaded Kernel. It also registers |
14 | the reboot notifier_block to trigger ima_update_kexec_buffer() at | 14 | the reboot notifier_block to trigger ima_update_kexec_buffer() at |
15 | kexec 'execute'. | 15 | kexec 'execute'. |
16 | |||
17 | Set the priority of register_reboot_notifier to INT_MIN to ensure that the | ||
18 | IMA log copy operation will happen at the end of the operation chain, which | ||
19 | is crucial for maintaining the integrity of the logs | ||
16 | 20 | ||
17 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 21 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
18 | Cc: Eric Biederman <ebiederm@xmission.com> | 22 | Cc: Eric Biederman <ebiederm@xmission.com> |
19 | Cc: Baoquan He <bhe@redhat.com> | 23 | Cc: Baoquan He <bhe@redhat.com> |
20 | Cc: Vivek Goyal <vgoyal@redhat.com> | 24 | Cc: Vivek Goyal <vgoyal@redhat.com> |
... | ... | ||
51 | +#include <linux/reboot.h> | 55 | +#include <linux/reboot.h> |
52 | +#include <asm/page.h> | 56 | +#include <asm/page.h> |
53 | #include "ima.h" | 57 | #include "ima.h" |
54 | 58 | ||
55 | #ifdef CONFIG_IMA_KEXEC | 59 | #ifdef CONFIG_IMA_KEXEC |
60 | +static bool ima_kexec_update_registered; | ||
56 | static struct seq_file ima_kexec_file; | 61 | static struct seq_file ima_kexec_file; |
57 | +static void *ima_kexec_buffer; | 62 | +static void *ima_kexec_buffer; |
58 | +static bool ima_kexec_update_registered; | 63 | |
59 | 64 | static void ima_free_kexec_file_buf(struct seq_file *sf) | |
60 | static void ima_reset_kexec_file(struct seq_file *sf) | ||
61 | { | 65 | { |
62 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 66 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
63 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", | 67 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", |
64 | kbuf.mem); | 68 | kbuf.mem); |
65 | } | 69 | } |
... | ... | ||
71 | + unsigned long action, void *data) | 75 | + unsigned long action, void *data) |
72 | +{ | 76 | +{ |
73 | + return NOTIFY_OK; | 77 | + return NOTIFY_OK; |
74 | +} | 78 | +} |
75 | + | 79 | + |
76 | +struct notifier_block update_buffer_nb = { | 80 | +static struct notifier_block update_buffer_nb = { |
77 | + .notifier_call = ima_update_kexec_buffer, | 81 | + .notifier_call = ima_update_kexec_buffer, |
78 | + .priority = 1, | 82 | + .priority = INT_MIN |
79 | +}; | 83 | +}; |
80 | + | 84 | + |
81 | +/* | 85 | +/* |
82 | + * Create a mapping for the source pages that contain the IMA buffer | 86 | + * Create a mapping for the source pages that contain the IMA buffer |
83 | + * so we can update it later. | 87 | + * so we can update it later. |
... | ... | diff view generated by jsdifflib |
1 | ima_dump_measurement_list() is called during kexec 'load', which may | 1 | ima_dump_measurement_list() is called during kexec 'load', which may |
---|---|---|---|
2 | result in loss of IMA measurements during kexec soft reboot. Due to | 2 | result in loss of IMA measurements during kexec soft reboot. Due to |
3 | missed measurements that only occurred after kexec 'load', this function | 3 | missed measurements that only occurred after kexec 'load', this function |
4 | needs to be called during kexec 'execute'. | 4 | needs to be called during kexec 'execute'. |
5 | 5 | ||
6 | This patch includes the following changes: | 6 | Make the kexec_segment_size variable a local static variable within the |
7 | - Implement kimage_file_post_load() function to be invoked after the new | 7 | file, so it can be accessed during both kexec 'load' and 'execute'. |
8 | kernel image has been loaded for kexec. | 8 | |
9 | - Call kimage_file_post_load() from kexec_file_load() syscall only for | 9 | Implement the kexec_post_load() function to be invoked after the new kernel |
10 | kexec soft reboot scenarios and not for KEXEC_FILE_ON_CRASH. It will | 10 | image has been loaded for kexec. Instead of calling machine_kexec_post_load() |
11 | map the IMA segment, and register reboot notifier for the function | 11 | directly from the kexec_file_load() syscall, call kexec_post_load(), which in |
12 | ima_update_kexec_buffer() which would copy the IMA log at kexec soft | 12 | turn calls machine_kexec_post_load() to maintain the original image processing. |
13 | reboot. | 13 | |
14 | - Make kexec_segment_size variable local static to the file so that it | 14 | Invoke ima_kexec_post_load() within the kexec_post_load() API only for kexec |
15 | becomes accessible both during kexec 'load' and 'execute'. | 15 | soft reboot scenarios, excluding KEXEC_FILE_ON_CRASH. |
16 | - Move ima_dump_measurement_list() call from ima_add_kexec_buffer() | 16 | |
17 | to ima_update_kexec_buffer(). | 17 | Register a reboot notifier for the ima_update_kexec_buffer() API within |
18 | - Copy the measurement list as much as possible. | 18 | ima_kexec_post_load() to ensure it is called upon receiving a reboot |
19 | - Remove ima_reset_kexec_file() call from ima_add_kexec_buffer(), now | 19 | notification. |
20 | that the buffer is being copied at kexec 'execute', and resetting the | 20 | |
21 | file at kexec 'load' would corrupt the buffer. | 21 | Move the ima_dump_measurement_list() call from ima_add_kexec_buffer() to |
22 | ima_update_kexec_buffer() to copy the IMA log at the kexec 'execute' stage. | ||
23 | |||
24 | When there is insufficient memory to copy all the measurement logs, copy as | ||
25 | much of the measurement list as possible. | ||
22 | 26 | ||
23 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 27 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
24 | Cc: Eric Biederman <ebiederm@xmission.com> | 28 | Cc: Eric Biederman <ebiederm@xmission.com> |
25 | Cc: Baoquan He <bhe@redhat.com> | 29 | Cc: Baoquan He <bhe@redhat.com> |
26 | Cc: Vivek Goyal <vgoyal@redhat.com> | 30 | Cc: Vivek Goyal <vgoyal@redhat.com> |
27 | Cc: Dave Young <dyoung@redhat.com> | 31 | Cc: Dave Young <dyoung@redhat.com> |
28 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 32 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
29 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | 33 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> |
30 | --- | 34 | --- |
31 | kernel/kexec_file.c | 10 +++++++ | 35 | kernel/kexec_file.c | 11 +++++++- |
32 | security/integrity/ima/ima_kexec.c | 48 ++++++++++++++++++------------ | 36 | security/integrity/ima/ima_kexec.c | 43 ++++++++++++++++++++---------- |
33 | 2 files changed, 39 insertions(+), 19 deletions(-) | 37 | 2 files changed, 39 insertions(+), 15 deletions(-) |
34 | 38 | ||
35 | diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c | 39 | diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c |
36 | index XXXXXXX..XXXXXXX 100644 | 40 | index XXXXXXX..XXXXXXX 100644 |
37 | --- a/kernel/kexec_file.c | 41 | --- a/kernel/kexec_file.c |
38 | +++ b/kernel/kexec_file.c | 42 | +++ b/kernel/kexec_file.c |
39 | @@ -XXX,XX +XXX,XX @@ kimage_validate_signature(struct kimage *image) | 43 | @@ -XXX,XX +XXX,XX @@ kimage_validate_signature(struct kimage *image) |
40 | } | 44 | } |
41 | #endif | 45 | #endif |
42 | 46 | ||
43 | +static void kimage_file_post_load(struct kimage *image) | 47 | +static int kexec_post_load(struct kimage *image, unsigned long flags) |
44 | +{ | 48 | +{ |
45 | +#ifdef CONFIG_IMA_KEXEC | 49 | +#ifdef CONFIG_IMA_KEXEC |
46 | + ima_kexec_post_load(image); | 50 | + if (!(flags & KEXEC_FILE_ON_CRASH)) |
51 | + ima_kexec_post_load(image); | ||
47 | +#endif | 52 | +#endif |
53 | + return machine_kexec_post_load(image); | ||
48 | +} | 54 | +} |
49 | + | 55 | + |
50 | /* | 56 | /* |
51 | * In file mode list of segments is prepared by kernel. Copy relevant | 57 | * In file mode list of segments is prepared by kernel. Copy relevant |
52 | * data from user space, do error checking, prepare segment list | 58 | * data from user space, do error checking, prepare segment list |
53 | @@ -XXX,XX +XXX,XX @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd, | 59 | @@ -XXX,XX +XXX,XX @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd, |
54 | 60 | ||
55 | kimage_terminate(image); | 61 | kimage_terminate(image); |
56 | 62 | ||
57 | + if (!(flags & KEXEC_FILE_ON_CRASH)) | 63 | - ret = machine_kexec_post_load(image); |
58 | + kimage_file_post_load(image); | 64 | + ret = kexec_post_load(image, flags); |
59 | + | ||
60 | ret = machine_kexec_post_load(image); | ||
61 | if (ret) | 65 | if (ret) |
62 | goto out; | 66 | goto out; |
67 | |||
63 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c | 68 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c |
64 | index XXXXXXX..XXXXXXX 100644 | 69 | index XXXXXXX..XXXXXXX 100644 |
65 | --- a/security/integrity/ima/ima_kexec.c | 70 | --- a/security/integrity/ima/ima_kexec.c |
66 | +++ b/security/integrity/ima/ima_kexec.c | 71 | +++ b/security/integrity/ima/ima_kexec.c |
67 | @@ -XXX,XX +XXX,XX @@ | 72 | @@ -XXX,XX +XXX,XX @@ |
68 | #ifdef CONFIG_IMA_KEXEC | 73 | #ifdef CONFIG_IMA_KEXEC |
74 | static bool ima_kexec_update_registered; | ||
69 | static struct seq_file ima_kexec_file; | 75 | static struct seq_file ima_kexec_file; |
76 | +static size_t kexec_segment_size; | ||
70 | static void *ima_kexec_buffer; | 77 | static void *ima_kexec_buffer; |
71 | +static size_t kexec_segment_size; | 78 | |
72 | static bool ima_kexec_update_registered; | 79 | static void ima_free_kexec_file_buf(struct seq_file *sf) |
73 | |||
74 | static void ima_reset_kexec_file(struct seq_file *sf) | ||
75 | @@ -XXX,XX +XXX,XX @@ static int ima_alloc_kexec_file_buf(size_t segment_size) | ||
76 | * compare the size of IMA measurement list with the size of the allocated memory | ||
77 | * if the size of the allocated memory is not less than the size of IMA measurement list | ||
78 | * copy the measurement list to the allocated memory. | ||
79 | - * else return error | ||
80 | + * else | ||
81 | + * copy the measurement list as much as possible. | ||
82 | */ | ||
83 | static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | ||
84 | unsigned long segment_size) | ||
85 | @@ -XXX,XX +XXX,XX @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | 80 | @@ -XXX,XX +XXX,XX @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, |
86 | } | 81 | } |
87 | } | 82 | } |
88 | 83 | ||
89 | - if (ret < 0) | 84 | - if (ret < 0) |
90 | - goto out; | 85 | - goto out; |
86 | - | ||
91 | /* | 87 | /* |
92 | * fill in reserved space with some buffer details | 88 | * fill in reserved space with some buffer details |
93 | * (eg. version, buffer size, number of measurements) | 89 | * (eg. version, buffer size, number of measurements) |
94 | @@ -XXX,XX +XXX,XX @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | 90 | @@ -XXX,XX +XXX,XX @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, |
91 | |||
95 | *buffer_size = ima_kexec_file.count; | 92 | *buffer_size = ima_kexec_file.count; |
96 | *buffer = ima_kexec_file.buf; | 93 | *buffer = ima_kexec_file.buf; |
97 | |||
98 | -out: | 94 | -out: |
95 | + | ||
99 | return ret; | 96 | return ret; |
100 | } | 97 | } |
101 | 98 | ||
102 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 99 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
100 | unsigned long binary_runtime_size; | ||
101 | |||
103 | /* use more understandable variable names than defined in kbuf */ | 102 | /* use more understandable variable names than defined in kbuf */ |
103 | + size_t kexec_buffer_size = 0; | ||
104 | void *kexec_buffer = NULL; | 104 | void *kexec_buffer = NULL; |
105 | size_t kexec_buffer_size = 0; | 105 | - size_t kexec_buffer_size; |
106 | - size_t kexec_segment_size; | 106 | - size_t kexec_segment_size; |
107 | int ret; | 107 | int ret; |
108 | 108 | ||
109 | /* | 109 | /* |
110 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 110 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
111 | return; | 111 | return; |
112 | } | 112 | } |
113 | 113 | ||
114 | - ret = ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, | 114 | - ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, |
115 | - kexec_segment_size); | 115 | - kexec_segment_size); |
116 | - if (ret < 0) { | 116 | - if (!kexec_buffer) { |
117 | - pr_err("Failed to dump IMA measurements. Error:%d.\n", ret); | 117 | - pr_err("Not enough memory for the kexec measurement buffer.\n"); |
118 | - return; | 118 | - return; |
119 | - } | 119 | - } |
120 | - | 120 | - |
121 | kbuf.buffer = kexec_buffer; | 121 | kbuf.buffer = kexec_buffer; |
122 | kbuf.bufsz = kexec_buffer_size; | 122 | kbuf.bufsz = kexec_buffer_size; |
123 | kbuf.memsz = kexec_segment_size; | 123 | kbuf.memsz = kexec_segment_size; |
124 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 124 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
125 | image->ima_segment_index = image->nr_segments - 1; | ||
126 | image->is_ima_segment_index_set = true; | ||
127 | |||
128 | - /* | ||
129 | - * kexec owns kexec_buffer after kexec_add_buffer() is called | ||
130 | - * and it will vfree() that buffer. | ||
131 | - */ | ||
132 | - ima_reset_kexec_file(&ima_kexec_file); | ||
133 | - | ||
134 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", | ||
135 | kbuf.mem); | ||
136 | } | ||
137 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | ||
138 | static int ima_update_kexec_buffer(struct notifier_block *self, | 125 | static int ima_update_kexec_buffer(struct notifier_block *self, |
139 | unsigned long action, void *data) | 126 | unsigned long action, void *data) |
140 | { | 127 | { |
141 | - return NOTIFY_OK; | 128 | - return NOTIFY_OK; |
142 | + void *buf = NULL; | ||
143 | + size_t buf_size = 0; | 129 | + size_t buf_size = 0; |
144 | + int ret = NOTIFY_OK; | 130 | + int ret = NOTIFY_OK; |
131 | + void *buf = NULL; | ||
145 | + | 132 | + |
146 | + if (!kexec_in_progress) { | 133 | + if (!kexec_in_progress) { |
147 | + pr_info("No kexec in progress.\n"); | 134 | + pr_info("No kexec in progress.\n"); |
148 | + return ret; | 135 | + return ret; |
149 | + } | 136 | + } |
... | ... | ||
165 | + ima_kexec_buffer = NULL; | 152 | + ima_kexec_buffer = NULL; |
166 | + | 153 | + |
167 | + return ret; | 154 | + return ret; |
168 | } | 155 | } |
169 | 156 | ||
170 | struct notifier_block update_buffer_nb = { | 157 | static struct notifier_block update_buffer_nb = { |
171 | -- | 158 | -- |
172 | 2.25.1 | 159 | 2.25.1 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | kexec 'load' may be called multiple times. Free and realloc the buffer | ||
2 | only if the segment_size is changed from the previous kexec 'load' call. | ||
1 | 3 | ||
4 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | ||
5 | --- | ||
6 | security/integrity/ima/ima_kexec.c | 10 ++++++++++ | ||
7 | 1 file changed, 10 insertions(+) | ||
8 | |||
9 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c | ||
10 | index XXXXXXX..XXXXXXX 100644 | ||
11 | --- a/security/integrity/ima/ima_kexec.c | ||
12 | +++ b/security/integrity/ima/ima_kexec.c | ||
13 | @@ -XXX,XX +XXX,XX @@ static void ima_free_kexec_file_buf(struct seq_file *sf) | ||
14 | |||
15 | static int ima_alloc_kexec_file_buf(size_t segment_size) | ||
16 | { | ||
17 | + /* | ||
18 | + * kexec 'load' may be called multiple times. | ||
19 | + * Free and realloc the buffer only if the segment_size is | ||
20 | + * changed from the previous kexec 'load' call. | ||
21 | + */ | ||
22 | + if (ima_kexec_file.buf && ima_kexec_file.size == segment_size) | ||
23 | + goto out; | ||
24 | + | ||
25 | ima_free_kexec_file_buf(&ima_kexec_file); | ||
26 | |||
27 | /* segment size can't change between kexec load and execute */ | ||
28 | @@ -XXX,XX +XXX,XX @@ static int ima_alloc_kexec_file_buf(size_t segment_size) | ||
29 | return -ENOMEM; | ||
30 | |||
31 | ima_kexec_file.size = segment_size; | ||
32 | + | ||
33 | +out: | ||
34 | ima_kexec_file.read_pos = 0; | ||
35 | ima_kexec_file.count = sizeof(struct ima_kexec_hdr); /* reserved space */ | ||
36 | |||
37 | -- | ||
38 | 2.25.1 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
15 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 15 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
16 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | 16 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> |
17 | Reviewed-by: Mimi Zohar <zohar@linux.ibm.com> | 17 | Reviewed-by: Mimi Zohar <zohar@linux.ibm.com> |
18 | --- | 18 | --- |
19 | security/integrity/ima/Kconfig | 10 ++++++++++ | 19 | security/integrity/ima/Kconfig | 10 ++++++++++ |
20 | security/integrity/ima/ima_kexec.c | 16 ++++++++++------ | 20 | security/integrity/ima/ima_kexec.c | 16 +++++++++++----- |
21 | 2 files changed, 20 insertions(+), 6 deletions(-) | 21 | 2 files changed, 21 insertions(+), 5 deletions(-) |
22 | 22 | ||
23 | diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig | 23 | diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig |
24 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/security/integrity/ima/Kconfig | 25 | --- a/security/integrity/ima/Kconfig |
26 | +++ b/security/integrity/ima/Kconfig | 26 | +++ b/security/integrity/ima/Kconfig |
... | ... | ||
45 | +++ b/security/integrity/ima/ima_kexec.c | 45 | +++ b/security/integrity/ima/ima_kexec.c |
46 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 46 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
47 | .buf_min = 0, .buf_max = ULONG_MAX, | 47 | .buf_min = 0, .buf_max = ULONG_MAX, |
48 | .top_down = true }; | 48 | .top_down = true }; |
49 | unsigned long binary_runtime_size; | 49 | unsigned long binary_runtime_size; |
50 | - | ||
51 | + unsigned long extra_memory; | 50 | + unsigned long extra_memory; |
51 | |||
52 | /* use more understandable variable names than defined in kbuf */ | 52 | /* use more understandable variable names than defined in kbuf */ |
53 | void *kexec_buffer = NULL; | ||
54 | size_t kexec_buffer_size = 0; | 53 | size_t kexec_buffer_size = 0; |
54 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | ||
55 | int ret; | 55 | int ret; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | - * Reserve an extra half page of memory for additional measurements | 58 | - * Reserve an extra half page of memory for additional measurements |
59 | - * added during the kexec load. | 59 | - * added during the kexec load. |
... | ... | ||
62 | - binary_runtime_size = ima_get_binary_runtime_size(); | 62 | - binary_runtime_size = ima_get_binary_runtime_size(); |
63 | + if (CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB <= 0) | 63 | + if (CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB <= 0) |
64 | + extra_memory = PAGE_SIZE / 2; | 64 | + extra_memory = PAGE_SIZE / 2; |
65 | + else | 65 | + else |
66 | + extra_memory = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024; | 66 | + extra_memory = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024; |
67 | + | ||
67 | + binary_runtime_size = ima_get_binary_runtime_size() + extra_memory; | 68 | + binary_runtime_size = ima_get_binary_runtime_size() + extra_memory; |
68 | + | 69 | + |
69 | if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE) | 70 | if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE) |
70 | kexec_segment_size = ULONG_MAX; | 71 | kexec_segment_size = ULONG_MAX; |
71 | else | 72 | else |
... | ... | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
35 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 35 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
36 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 36 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
37 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | 37 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> |
38 | --- | 38 | --- |
39 | security/integrity/ima/ima.h | 6 ++++++ | 39 | security/integrity/ima/ima.h | 6 ++++++ |
40 | security/integrity/ima/ima_kexec.c | 20 ++++++++++++++++++++ | 40 | security/integrity/ima/ima_kexec.c | 21 +++++++++++++++++++++ |
41 | security/integrity/ima/ima_queue.c | 5 +++++ | 41 | security/integrity/ima/ima_queue.c | 5 +++++ |
42 | 3 files changed, 31 insertions(+) | 42 | 3 files changed, 32 insertions(+) |
43 | 43 | ||
44 | diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h | 44 | diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h |
45 | index XXXXXXX..XXXXXXX 100644 | 45 | index XXXXXXX..XXXXXXX 100644 |
46 | --- a/security/integrity/ima/ima.h | 46 | --- a/security/integrity/ima/ima.h |
47 | +++ b/security/integrity/ima/ima.h | 47 | +++ b/security/integrity/ima/ima.h |
... | ... | ||
66 | #include "ima.h" | 66 | #include "ima.h" |
67 | 67 | ||
68 | #ifdef CONFIG_IMA_KEXEC | 68 | #ifdef CONFIG_IMA_KEXEC |
69 | +#define IMA_KEXEC_EVENT_LEN 256 | 69 | +#define IMA_KEXEC_EVENT_LEN 256 |
70 | + | 70 | + |
71 | static bool ima_kexec_update_registered; | ||
71 | static struct seq_file ima_kexec_file; | 72 | static struct seq_file ima_kexec_file; |
72 | static void *ima_kexec_buffer; | ||
73 | static size_t kexec_segment_size; | 73 | static size_t kexec_segment_size; |
74 | @@ -XXX,XX +XXX,XX @@ static void ima_free_kexec_file_buf(struct seq_file *sf) | 74 | @@ -XXX,XX +XXX,XX @@ static void ima_free_kexec_file_buf(struct seq_file *sf) |
75 | ima_reset_kexec_file(sf); | 75 | sf->count = 0; |
76 | } | 76 | } |
77 | 77 | ||
78 | +void ima_measure_kexec_event(const char *event_name) | 78 | +void ima_measure_kexec_event(const char *event_name) |
79 | +{ | 79 | +{ |
80 | + char ima_kexec_event[IMA_KEXEC_EVENT_LEN]; | 80 | + char ima_kexec_event[IMA_KEXEC_EVENT_LEN]; |
81 | + size_t buf_size = 0; | 81 | + size_t buf_size = 0; |
82 | + long len; | 82 | + long len; |
83 | + int n; | ||
83 | + | 84 | + |
84 | + buf_size = ima_get_binary_runtime_size(); | 85 | + buf_size = ima_get_binary_runtime_size(); |
85 | + len = atomic_long_read(&ima_htable.len); | 86 | + len = atomic_long_read(&ima_htable.len); |
86 | + | 87 | + |
87 | + int n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN, | 88 | + n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN, |
88 | + "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;" | 89 | + "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;" |
89 | + "ima_runtime_measurements_count=%ld;", | 90 | + "ima_runtime_measurements_count=%ld;", |
90 | + kexec_segment_size, buf_size, len); | 91 | + kexec_segment_size, buf_size, len); |
91 | + | 92 | + |
92 | + ima_measure_critical_data("ima_kexec", event_name, ima_kexec_event, n, false, NULL, 0); | 93 | + ima_measure_critical_data("ima_kexec", event_name, ima_kexec_event, n, false, NULL, 0); |
93 | +} | 94 | +} |
94 | + | 95 | + |
95 | static int ima_alloc_kexec_file_buf(size_t segment_size) | 96 | static int ima_alloc_kexec_file_buf(size_t segment_size) |
... | ... | diff view generated by jsdifflib |