From: Rakesh Kudurumalla <[email protected]>

Make sure that out-of-bound access does not happen by saving one byte in
buffer for NUL terminator.

Fixes: a8926a65ad1d ("pmu: support Arm")
Fixes: 960c43184c4d ("pmu: introduce library for reading PMU events")
Cc: [email protected]

Signed-off-by: Tomasz Duszynski <[email protected]>
Signed-off-by: Rakesh Kudurumalla <[email protected]>
---
 lib/pmu/pmu.c       | 54 +++++++++++++++++++++++++++++++++++++++++++--
 lib/pmu/pmu_arm64.c |  3 ++-
 lib/pmu/rte_pmu.h   | 12 +++++++++-
 3 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/lib/pmu/pmu.c b/lib/pmu/pmu.c
index c14ffa5a2a..3f57d72a53 100644
--- a/lib/pmu/pmu.c
+++ b/lib/pmu/pmu.c
@@ -126,7 +126,7 @@ get_event_config(const char *name, uint64_t config[3])
        if (fp == NULL)
                return -errno;
 
-       ret = fread(buf, 1, sizeof(buf), fp);
+       ret = fread(buf, 1, sizeof(buf) - 1, fp);
        if (ret == 0) {
                fclose(fp);
 
@@ -419,6 +419,45 @@ rte_pmu_add_event(const char *name)
        return event->index;
 }
 
+static int
+check_perf_permissions(void)
+{
+       const char *paranoid_path = "/proc/sys/kernel/perf_event_paranoid";
+       int level, ret;
+       FILE *fp;
+
+       /* Check if user is root */
+       if (getuid() == 0)
+               return 0;  /* Root has access */
+
+       fp = fopen(paranoid_path, "r");
+       if (!fp)
+               return -ENOENT;  /* File doesn't exist */
+
+       ret = fscanf(fp, "%d", &level);
+       fclose(fp);
+
+       if (ret != 1)
+               return -EINVAL;
+
+       /* On vanilla Linux the default perf_event_paranoid level is 2, which 
allows non-privileged
+        * processes to access performance counters.
+        *
+        * Debian / Ubuntu and their derivatives apply patches that introduce
+        * additional paranoia levels:
+        *
+        * - Debian adds level 3, which restricts access to perf_event_open() 
for
+        *   monitoring other processes, but still allows unprivileged 
self-monitoring.
+        *   See: 
https://lore.kernel.org/all/[email protected]/
+        * - Ubuntu adds level 4 (which is also the default), completely 
disabling perf_event_open()
+        *   for unprivileged users???effectively disabling self-monitoring.
+        */
+       if (level >= 4)
+               return -EACCES;  /* Insufficient privileges */
+
+       return 0;  /* Allowed */
+}
+
 static int
 add_events(const char *pattern)
 {
@@ -492,10 +531,21 @@ rte_pmu_init(void)
        if (rte_pmu.initialized && ++rte_pmu.initialized)
                return 0;
 
+       /* Check permissions first */
+       ret = check_perf_permissions();
+       if (ret == -EACCES) {
+               PMU_LOG(ERR, "Insufficient privileges for PMU access (check 
perf_event_paranoid)");
+               return -EACCES;
+       }
+       if (ret == -ENOENT) {
+               PMU_LOG(ERR, "Cannot access perf_event_paranoid file");
+               return -ENODEV;
+       }
+
        ret = scan_pmus();
        if (ret) {
                PMU_LOG(ERR, "Failed to scan for event sources");
-               goto out;
+               return -ENODEV;  /* No PMU hardware found */
        }
 
        ret = pmu_arch_init();
diff --git a/lib/pmu/pmu_arm64.c b/lib/pmu/pmu_arm64.c
index 2c40b5f702..f3a817b42f 100644
--- a/lib/pmu/pmu_arm64.c
+++ b/lib/pmu/pmu_arm64.c
@@ -24,12 +24,13 @@ read_attr_int(const char *path, int *val)
        if (fd == -1)
                return -errno;
 
-       ret = read(fd, buf, sizeof(buf));
+       ret = read(fd, buf, sizeof(buf) - 1);
        if (ret == -1) {
                close(fd);
 
                return -errno;
        }
+       buf[ret] = '\0';
 
        *val = strtol(buf, NULL, 10);
        close(fd);
diff --git a/lib/pmu/rte_pmu.h b/lib/pmu/rte_pmu.h
index 9970282c76..0c2a5d9298 100644
--- a/lib/pmu/rte_pmu.h
+++ b/lib/pmu/rte_pmu.h
@@ -150,10 +150,20 @@ __rte_pmu_enable_group(struct rte_pmu_event_group *group);
  *
  * Initialize PMU library.
  *
+ * This function initializes the Performance Monitoring Unit (PMU) library
+ * by checking permissions, scanning for available PMU hardware, and setting up
+ * necessary architecture-specific internals.
+ *
  * It's safe to call it multiple times.
  *
  * @return
- *   0 in case of success, negative value otherwise.
+ *   0 on success.
+ *   -ENODEV if PMU hardware is not available or kernel lacks PMU support.
+ *   -EACCES if insufficient privileges to access PMU.
+ *           User should check /proc/sys/kernel/perf_event_paranoid settings.
+ *   -ENOMEM if memory allocation failed.
+ *   -EINVAL if invalid configuration detected.
+ *   Other negative errno values on other failures.
  */
 __rte_experimental
 int
-- 
2.25.1

Reply via email to