On 1/27/21 1:25 PM, Yu-cheng Yu wrote:
arch_prctl(ARCH_X86_CET_STATUS, u64 *args)
Get CET feature status.
The parameter 'args' is a pointer to a user buffer. The kernel returns
the following information:
*args = shadow stack/IBT status
*(args + 1) = shadow stack base address
*(args + 2) = shadow stack size
What's the deal for 32-bit binaries? The in-kernel code looks 64-bit
only, but I don't see anything restricting the interface to 64-bit.
+static int copy_status_to_user(struct cet_status *cet, u64 arg2)
This has static scope, but it's still awfully generically named. A cet_
prefix would be nice.
+{
+ u64 buf[3] = {0, 0, 0};
+
+ if (cet->shstk_size) {
+ buf[0] |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+ buf[1] = (u64)cet->shstk_base;
+ buf[2] = (u64)cet->shstk_size;
What's the casting for?
+ }
+
+ return copy_to_user((u64 __user *)arg2, buf, sizeof(buf));
+}
+
+int prctl_cet(int option, u64 arg2)
+{
+ struct cet_status *cet;
+ unsigned int features;
+
+ /*
+ * GLIBC's ENOTSUPP == EOPNOTSUPP == 95, and it does not recognize
+ * the kernel's ENOTSUPP (524). So return EOPNOTSUPP here.
+ */
+ if (!IS_ENABLED(CONFIG_X86_CET))
+ return -EOPNOTSUPP;
Let's ignore glibc for a moment. What error code *should* the kernel be
returning here? errno(3) says:
EOPNOTSUPP Operation not supported on socket (POSIX.1)
...
ENOTSUP Operation not supported (POSIX.1)
+ cet = ¤t->thread.cet;
+
+ if (option == ARCH_X86_CET_STATUS)
+ return copy_status_to_user(cet, arg2);
What's the point of doing copy_status_to_user() if the processor doesn't
support CET? In other words, shouldn't this be below the CPU feature check?
Also, please cast arg2 *here*. It becomes a user pointer here, not at
the copy_to_user().
+ if (!static_cpu_has(X86_FEATURE_CET))
+ return -EOPNOTSUPP;
So, you went to the trouble of adding a disabled-features.h entry for
this. Why not just do:
if (cpu_feature_enabled(X86_FEATURE_CET))
...
instead of the IS_ENABLED() check above? That should get rid of one of
these if's.
+ switch (option) {
+ case ARCH_X86_CET_DISABLE:
+ if (cet->locked)
+ return -EPERM;
+
+ features = (unsigned int)arg2;
What's the purpose of this cast?
+ if (features & ~GNU_PROPERTY_X86_FEATURE_1_VALID)
+ return -EINVAL;
+ if (features & GNU_PROPERTY_X86_FEATURE_1_SHSTK)
+ cet_disable_shstk();
+ return 0;
This doesn't enforce that the high bits of arg2 be 0. Shouldn't we call
them reserved and enforce that they be 0?
+ case ARCH_X86_CET_LOCK:
+ cet->locked = 1;
+ return 0;
This needs to check for and enforce that arg2==0.
+ default:
+ return -ENOSYS;
+ }
+}