Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/pseries/papr-sysparm: Validate buffer object lengths

The ability to get and set system parameters will be exposed to user
space, so let's get a little more strict about malformed
papr_sysparm_buf objects.

* Create accessors for the length field of struct papr_sysparm_buf.
The length is always stored in MSB order and this is better than
spreading the necessary conversions all over.

* Reject attempts to submit invalid buffers to RTAS.

* Warn if RTAS returns a buffer with an invalid length, clamping the
returned length to a safe value that won't overrun the buffer.

These are meant as precautionary measures to mitigate both firmware
and kernel bugs in this area, should they arise, but I am not aware of
any.

Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20231212-papr-sys_rtas-vs-lockdown-v6-10-e9eafd0c8c6c@linux.ibm.com

authored by

Nathan Lynch and committed by
Michael Ellerman
35aae182 514f6ff4

+47
+47
arch/powerpc/platforms/pseries/papr-sysparm.c
··· 23 23 kfree(buf); 24 24 } 25 25 26 + static size_t papr_sysparm_buf_get_length(const struct papr_sysparm_buf *buf) 27 + { 28 + return be16_to_cpu(buf->len); 29 + } 30 + 31 + static void papr_sysparm_buf_set_length(struct papr_sysparm_buf *buf, size_t length) 32 + { 33 + WARN_ONCE(length > sizeof(buf->val), 34 + "bogus length %zu, clamping to safe value", length); 35 + length = min(sizeof(buf->val), length); 36 + buf->len = cpu_to_be16(length); 37 + } 38 + 39 + /* 40 + * For use on buffers returned from ibm,get-system-parameter before 41 + * returning them to callers. Ensures the encoded length of valid data 42 + * cannot overrun buf->val[]. 43 + */ 44 + static void papr_sysparm_buf_clamp_length(struct papr_sysparm_buf *buf) 45 + { 46 + papr_sysparm_buf_set_length(buf, papr_sysparm_buf_get_length(buf)); 47 + } 48 + 49 + /* 50 + * Perform some basic diligence on the system parameter buffer before 51 + * submitting it to RTAS. 52 + */ 53 + static bool papr_sysparm_buf_can_submit(const struct papr_sysparm_buf *buf) 54 + { 55 + /* 56 + * Firmware ought to reject buffer lengths that exceed the 57 + * maximum specified in PAPR, but there's no reason for the 58 + * kernel to allow them either. 59 + */ 60 + if (papr_sysparm_buf_get_length(buf) > sizeof(buf->val)) 61 + return false; 62 + 63 + return true; 64 + } 65 + 26 66 /** 27 67 * papr_sysparm_get() - Retrieve the value of a PAPR system parameter. 28 68 * @param: PAPR system parameter token as described in ··· 103 63 if (token == RTAS_UNKNOWN_SERVICE) 104 64 return -ENOENT; 105 65 66 + if (!papr_sysparm_buf_can_submit(buf)) 67 + return -EINVAL; 68 + 106 69 work_area = rtas_work_area_alloc(sizeof(*buf)); 107 70 108 71 memcpy(rtas_work_area_raw_buf(work_area), buf, sizeof(*buf)); ··· 120 77 case 0: 121 78 ret = 0; 122 79 memcpy(buf, rtas_work_area_raw_buf(work_area), sizeof(*buf)); 80 + papr_sysparm_buf_clamp_length(buf); 123 81 break; 124 82 case -3: /* parameter not implemented */ 125 83 ret = -EOPNOTSUPP; ··· 158 114 159 115 if (token == RTAS_UNKNOWN_SERVICE) 160 116 return -ENOENT; 117 + 118 + if (!papr_sysparm_buf_can_submit(buf)) 119 + return -EINVAL; 161 120 162 121 work_area = rtas_work_area_alloc(sizeof(*buf)); 163 122