Re: [PATCH bpf v3 2/6] libbpf: Fix memory leak in parse_usdt_arg()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 10/11/2022 9:34 AM, Andrii Nakryiko wrote:
On Mon, Oct 10, 2022 at 7:08 AM Xu Kuohai <xukuohai@xxxxxxxxxx> wrote:

In the arm64 version of parse_usdt_arg(), when sscanf returns 2, reg_name
is allocated but not freed. Fix it.

Fixes: 0f8619929c57 ("libbpf: Usdt aarch64 arg parsing support")
Signed-off-by: Xu Kuohai <xukuohai@xxxxxxxxxx>
---
  tools/lib/bpf/usdt.c | 59 +++++++++++++++++++++++++-------------------
  1 file changed, 33 insertions(+), 26 deletions(-)

diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c
index e83b497c2245..f3b5be7415b5 100644
--- a/tools/lib/bpf/usdt.c
+++ b/tools/lib/bpf/usdt.c
@@ -1351,8 +1351,10 @@ static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec
         char *reg_name = NULL;
         int arg_sz, len, reg_off;
         long off;
+       int ret;

-       if (sscanf(arg_str, " %d @ \[ %m[a-z0-9], %ld ] %n", &arg_sz, &reg_name, &off, &len) == 3) {
+       ret = sscanf(arg_str, " %d @ \[ %m[a-z0-9], %ld ] %n", &arg_sz, &reg_name, &off, &len);
+       if (ret == 3) {
                 /* Memory dereference case, e.g., -4@[sp, 96] */
                 arg->arg_type = USDT_ARG_REG_DEREF;
                 arg->val_off = off;
@@ -1361,32 +1363,37 @@ static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec
                 if (reg_off < 0)
                         return reg_off;
                 arg->reg_off = reg_off;
-       } else if (sscanf(arg_str, " %d @ \[ %m[a-z0-9] ] %n", &arg_sz, &reg_name, &len) == 2) {
-               /* Memory dereference case, e.g., -4@[sp] */
-               arg->arg_type = USDT_ARG_REG_DEREF;
-               arg->val_off = 0;
-               reg_off = calc_pt_regs_off(reg_name);
-               free(reg_name);
-               if (reg_off < 0)
-                       return reg_off;
-               arg->reg_off = reg_off;
-       } else if (sscanf(arg_str, " %d @ %ld %n", &arg_sz, &off, &len) == 2) {
-               /* Constant value case, e.g., 4@5 */
-               arg->arg_type = USDT_ARG_CONST;
-               arg->val_off = off;
-               arg->reg_off = 0;
-       } else if (sscanf(arg_str, " %d @ %m[a-z0-9] %n", &arg_sz, &reg_name, &len) == 2) {
-               /* Register read case, e.g., -8@x4 */
-               arg->arg_type = USDT_ARG_REG;
-               arg->val_off = 0;
-               reg_off = calc_pt_regs_off(reg_name);
-               free(reg_name);
-               if (reg_off < 0)
-                       return reg_off;
-               arg->reg_off = reg_off;
         } else {
-               pr_warn("usdt: unrecognized arg #%d spec '%s'\n", arg_num, arg_str);
-               return -EINVAL;
+               if (ret == 2)
+                       free(reg_name);
+
+               if (sscanf(arg_str, " %d @ \[ %m[a-z0-9] ] %n", &arg_sz, &reg_name, &len) == 2) {
+                       /* Memory dereference case, e.g., -4@[sp] */
+                       arg->arg_type = USDT_ARG_REG_DEREF;
+                       arg->val_off = 0;
+                       reg_off = calc_pt_regs_off(reg_name);
+                       free(reg_name);
+                       if (reg_off < 0)
+                               return reg_off;
+                       arg->reg_off = reg_off;
+               } else if (sscanf(arg_str, " %d @ %ld %n", &arg_sz, &off, &len) == 2) {
+                       /* Constant value case, e.g., 4@5 */
+                       arg->arg_type = USDT_ARG_CONST;
+                       arg->val_off = off;
+                       arg->reg_off = 0;
+               } else if (sscanf(arg_str, " %d @ %m[a-z0-9] %n", &arg_sz, &reg_name, &len) == 2) {
+                       /* Register read case, e.g., -8@x4 */
+                       arg->arg_type = USDT_ARG_REG;
+                       arg->val_off = 0;
+                       reg_off = calc_pt_regs_off(reg_name);
+                       free(reg_name);
+                       if (reg_off < 0)
+                               return reg_off;
+                       arg->reg_off = reg_off;
+               } else {
+                       pr_warn("usdt: unrecognized arg #%d spec '%s'\n", arg_num, arg_str);
+                       return -EINVAL;
+               }
         }


I think all this is more complicated than it has to be. How big  can
register names be? Few characters? Let's get rid of %m[a-z0-9] and
instead use fixed-max-length strings, e.g., %5s. And read register
names into such local char buffers. It will simplify everything
tremendously. Let's use 16-byte buffers and use %15s to match it?
Would that be enough?


The valid register names accepted by calc_pt_regs_off() are x0~x31 and sp, so
16-byte buffer is enough. Since %15s matches all non-space characters, will use
%15[a-z0-9] to match it.

         arg->arg_signed = arg_sz < 0;
--
2.30.2

.




[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux