Skip to content

Commit

Permalink
powerpc/pseries/papr-sysparm: Validate buffer object lengths
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Nathan Lynch authored and Michael Ellerman committed Dec 13, 2023
1 parent 514f6ff commit 35aae18
Showing 1 changed file with 47 additions and 0 deletions.
47 changes: 47 additions & 0 deletions arch/powerpc/platforms/pseries/papr-sysparm.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,46 @@ void papr_sysparm_buf_free(struct papr_sysparm_buf *buf)
kfree(buf);
}

static size_t papr_sysparm_buf_get_length(const struct papr_sysparm_buf *buf)
{
return be16_to_cpu(buf->len);
}

static void papr_sysparm_buf_set_length(struct papr_sysparm_buf *buf, size_t length)
{
WARN_ONCE(length > sizeof(buf->val),
"bogus length %zu, clamping to safe value", length);
length = min(sizeof(buf->val), length);
buf->len = cpu_to_be16(length);
}

/*
* For use on buffers returned from ibm,get-system-parameter before
* returning them to callers. Ensures the encoded length of valid data
* cannot overrun buf->val[].
*/
static void papr_sysparm_buf_clamp_length(struct papr_sysparm_buf *buf)
{
papr_sysparm_buf_set_length(buf, papr_sysparm_buf_get_length(buf));
}

/*
* Perform some basic diligence on the system parameter buffer before
* submitting it to RTAS.
*/
static bool papr_sysparm_buf_can_submit(const struct papr_sysparm_buf *buf)
{
/*
* Firmware ought to reject buffer lengths that exceed the
* maximum specified in PAPR, but there's no reason for the
* kernel to allow them either.
*/
if (papr_sysparm_buf_get_length(buf) > sizeof(buf->val))
return false;

return true;
}

/**
* papr_sysparm_get() - Retrieve the value of a PAPR system parameter.
* @param: PAPR system parameter token as described in
Expand Down Expand Up @@ -63,6 +103,9 @@ int papr_sysparm_get(papr_sysparm_t param, struct papr_sysparm_buf *buf)
if (token == RTAS_UNKNOWN_SERVICE)
return -ENOENT;

if (!papr_sysparm_buf_can_submit(buf))
return -EINVAL;

work_area = rtas_work_area_alloc(sizeof(*buf));

memcpy(rtas_work_area_raw_buf(work_area), buf, sizeof(*buf));
Expand All @@ -77,6 +120,7 @@ int papr_sysparm_get(papr_sysparm_t param, struct papr_sysparm_buf *buf)
case 0:
ret = 0;
memcpy(buf, rtas_work_area_raw_buf(work_area), sizeof(*buf));
papr_sysparm_buf_clamp_length(buf);
break;
case -3: /* parameter not implemented */
ret = -EOPNOTSUPP;
Expand Down Expand Up @@ -115,6 +159,9 @@ int papr_sysparm_set(papr_sysparm_t param, const struct papr_sysparm_buf *buf)
if (token == RTAS_UNKNOWN_SERVICE)
return -ENOENT;

if (!papr_sysparm_buf_can_submit(buf))
return -EINVAL;

work_area = rtas_work_area_alloc(sizeof(*buf));

memcpy(rtas_work_area_raw_buf(work_area), buf, sizeof(*buf));
Expand Down

0 comments on commit 35aae18

Please sign in to comment.