Compare commits

...

6 Commits

Author SHA1 Message Date
Linus Torvalds
28924df2a0 Merge tag 'perf-tools-fixes-for-v7.3-2026-09-07' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools
Pull perf tools fixes from Namhyung Kim:
 "Two simple fixes for this cycle:

   - Do not use separate debug files for Intel PT decoding

   - Fix size of raw data in the PowerPC VPA DTL samples"

* tag 'perf-tools-fixes-for-v7.3-2026-09-07' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools:
  perf powerpc-vpadtl: Fix raw_size of DTL samples
  perf symbol: Do not use debug file as the binary type
2026-09-07 10:26:56 -07:00
Linus Torvalds
c297ed90fb Merge tag 'configfs-7.3-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/leitao/linux
Pull configfs fixes from Breno Leitao:

 - A symlink racing with rmdir of its target could reach a freed
   ->ci_dentry.

   The reference that get_target() takes pins the config_item, not
   its dentry; the dentry is pinned by DCACHE_PERSISTENT, which
   configfs_remove_dir() drops while the item is still alive.

   Take the target's configfs_dirent under ->d_lock instead of chasing
   ->ci_dentry.

 - configfs_rmdir() left the dentry hashed across the final put of the
   item, and configfs_get_config_item() treats a hashed dentry as proof
   of a live item. A concurrent symlink could therefore resurrect a
   dying item and hit a use-after-free.

   Unhash in configfs_remove_dir(), while the item is still guaranteed
   to be there.

Both issues were found by syzbot.

* tag 'configfs-7.3-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/leitao/linux:
  configfs: unhash the dentry before dropping the item in rmdir
  configfs: pin the symlink target's dirent instead of chasing ->ci_dentry
2026-09-07 10:01:38 -07:00
Vasileios Almpanis
f06c2d26d1 configfs: unhash the dentry before dropping the item in rmdir
configfs_get_config_item() treats a hashed dentry as proof that
sd->s_element is a live config_item.  configfs_rmdir() breaks that:
simple_rmdir() leaves the dentry hashed, the last reference to the item is
dropped right after, and the dentry is only unhashed by d_delete() once
->rmdir() has returned.  configfs_symlink() resolves its target holding no
lock on it, so get_target() can land in that window:

  BUG: KASAN: slab-use-after-free in config_item_get+0x26/0x90
   get_target fs/configfs/symlink.c:128 [inline]
   configfs_symlink+0x4ab/0x1030 fs/configfs/symlink.c:185

Unhash in configfs_remove_dir(), while the item is still guaranteed to be
there.  A reference obtained just before that stays harmless, as
create_link() rechecks CONFIGFS_USET_DROPPING, already set by
configfs_detach_prep().  Both configfs_unregister_subsystem() paths
d_drop() after detaching, so this only makes rmdir match them.

Reported-by: syzbot+6b16e3d085833cbf3e25@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=6b16e3d085833cbf3e25
Fixes: 7063fbf226 ("[PATCH] configfs: User-driven configuration filesystem")
Cc: stable@vger.kernel.org
Signed-off-by: Vasileios Almpanis <vasilisalmpanis@gmail.com>
Tested-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Breno Leitao <leitao@debian.org>
Link: https://patch.msgid.link/20260730093435.195441-3-vasilisalmpanis@gmail.com
Signed-off-by: Breno Leitao <leitao@debian.org>
2026-09-07 09:15:05 -07:00
Vasileios Almpanis
a7c1290eef configfs: pin the symlink target's dirent instead of chasing ->ci_dentry
create_link() reads the target's configfs_dirent from
item->ci_dentry->d_fsdata, relying on the item reference taken by
get_target().  That reference pins the item, not its dentry: the dentry is
pinned by DCACHE_PERSISTENT, which configfs_remove_dir() releases via
simple_rmdir() while the item is still alive.  A symlink racing with rmdir
of its target can therefore find ->ci_dentry freed and its dirent
released, triggering WARN_ON(!atomic_read(&sd->s_count)) in configfs_get().

Take the dirent in get_target() as well, under ->d_lock and atomically
with the item reference, and pass it down to create_link().  A hashed
dentry has not been killed yet, so its ->d_fsdata reference keeps the
dirent alive there.

Cc: stable@vger.kernel.org
Fixes: 7063fbf226 ("[PATCH] configfs: User-driven configuration filesystem")
Signed-off-by: Vasileios Almpanis <vasilisalmpanis@gmail.com>
Tested-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Breno Leitao <leitao@debian.org>
Link: https://patch.msgid.link/20260730093435.195441-2-vasilisalmpanis@gmail.com
Signed-off-by: Breno Leitao <leitao@debian.org>
2026-09-07 09:14:59 -07:00
Wang Yan
aadea57f53 perf powerpc-vpadtl: Fix raw_size of DTL samples
In powerpc_vpadtl_sample(), raw_data of the synthetic sample points to a
struct powerpc_vpadtl_entry (48 bytes), but raw_size is set to
sizeof(record).  record is a struct powerpc_vpadtl_entry pointer, so
sizeof(record) is the size of the pointer (8 bytes on 64-bit) rather
than the size of the record itself.

As a result, consumers that bound their access to raw_data by raw_size
only see or copy the first 8 bytes of each DTL entry instead of the full
record.

Use sizeof(*record) so that raw_size reflects the actual length of the
raw data.

Fixes: 8644834a48 ("perf powerpc: Process the DTL entries in queue and deliver samples")
Signed-off-by: Wang Yan <wangyan01@kylinos.cn>
Reviewed-by: Athira Rajeev <atrajeev@linux.ibm.com>
Reviewed-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
2026-08-30 23:11:35 -07:00
Adrian Hunter
ae9464c65e perf symbol: Do not use debug file as the binary type
dso__load() sets the binary type of a DSO to the type of the first symbol
source found. For a DSO with a separate debug file linked via
.gnu-debuglink, that is DSO_BINARY_TYPE__DEBUGLINK, which makes
dso__get_filename() return the name of the debug file instead of the file
that was actually executed.

Consumers that need to read instruction bytes, such as Intel PT decoding
in 'perf script', then read from the debug file and produce wrong
instructions.

Prefer DSO_BINARY_TYPE__BUILD_ID_CACHE, and otherwise
DSO_BINARY_TYPE__SYSTEM_PATH_DSO, over debug-only types, which restores
the behaviour of using a file that contains the executed instructions.

This is a workaround. Properly separating the binary file used for
instructions from the file used for debug symbols is left for later.

Example:

 Create a shared object with a separate .gnu_debuglink debug file. Note
 that 'objcopy --only-keep-debug' leaves .text as NOBITS, so instructions
 read from the debug file are zeros:

  # cat > foo.c << EOF
  unsigned long foo_work(unsigned long n)
  {
        unsigned long s = 0;

        for (unsigned long i = 0; i < n; i++)
                s = s * 31 + i;
        return s;
  }
  EOF
  # cat > main.c << EOF
  #include <stdio.h>
  unsigned long foo_work(unsigned long n);
  int main(void)
  {
        printf("%lu\n", foo_work(1000));
        return 0;
  }
  EOF
  # gcc -g -O2 -shared -fPIC -o libfoo.so foo.c
  # gcc -g -O2 -o main main.c -L. -lfoo -Wl,-rpath,'$ORIGIN'
  # objcopy --only-keep-debug libfoo.so libfoo.so.debug
  # objcopy --strip-debug libfoo.so
  # objcopy --add-gnu-debuglink=libfoo.so.debug libfoo.so
  # perf record -e intel_pt//u ./main

 Note that branch samples must be requested, because it is the resolving
 of the branch target symbol that causes dso__load() to be called, and
 hence the binary type to be set, before the decoder walks the code.
 With '--itrace=e' alone, nothing loads symbols for libfoo.so, the binary
 type is left as DSO_BINARY_TYPE__NOT_FOUND, the correct file is read
 anyway, and no errors are reported either way.

 Before:

  # perf.before script --itrace=be 2>&1 | grep "instruction trace error"
   instruction trace error type 1 time 2350.467489498 cpu 9 pid 75634 tid 75634 ip 0x77d48480718f code 6: Trace doesn't match instruction
   instruction trace error type 1 time 2350.467489832 cpu 9 pid 75634 tid 75634 ip 0x77d484807341 code 6: Trace doesn't match instruction
   instruction trace error type 1 time 2350.467496412 cpu 9 pid 75634 tid 75634 ip 0x5b4de37a8074 code 6: Trace doesn't match instruction
   instruction trace error type 1 time 2350.467593393 cpu 9 pid 75634 tid 75634 ip 0x77d4848070d0 code 6: Trace doesn't match instruction
   instruction trace error type 1 time 2350.467593954 cpu 9 pid 75634 tid 75634 ip 0x77d4848075a8 code 6: Trace doesn't match instruction
   instruction trace error type 1 time 2350.467595728 cpu 9 pid 75634 tid 75634 ip 0x77d4848324de code 6: Trace doesn't match instruction
  6 instruction trace errors

 After:

  # perf script --itrace=be 2>&1 | grep "instruction trace error"
  #

Fixes: 5363c30678 ("perf symbol: Set binary_type of dso when loading")
Reported-by: Todd Lipcon <tlipcon@google.com>
Closes: https://lore.kernel.org/all/CAGH6UiG=RJLqBU3kLu9XJciPyPO1HZkbAPERguVUMRuWQgqf=A@mail.gmail.com/
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
2026-08-30 22:55:56 -07:00
4 changed files with 40 additions and 6 deletions

View File

@@ -416,6 +416,15 @@ static void configfs_remove_dir(struct dentry *d)
if (d_really_is_positive(d)) {
if (unlikely(simple_rmdir(d_inode(parent), d)))
pr_warn("remove_dir (%pd): attributes remain", d);
else
/*
* configfs_get_config_item() takes a hashed dentry as
* proof that ->s_element is still alive. Our caller
* is about to drop the last reference to the item and
* the VFS will not unhash until after we return, so
* unhash it here.
*/
d_drop(d);
}
pr_debug(" o %pd removing done (%d)\n", d, d_count(d));

View File

@@ -76,9 +76,9 @@ static int configfs_get_target_path(struct config_item *item,
static int create_link(struct config_item *parent_item,
struct config_item *item,
struct configfs_dirent *target_sd,
struct dentry *dentry)
{
struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata;
char *body;
int ret;
@@ -115,6 +115,7 @@ static int create_link(struct config_item *parent_item,
static int get_target(const char *symname, struct config_item **target,
struct configfs_dirent **target_sd,
struct super_block *sb)
{
struct path path __free(path_put) = {};
@@ -125,7 +126,20 @@ static int get_target(const char *symname, struct config_item **target,
return ret;
if (path.dentry->d_sb != sb)
return -EPERM;
*target = configfs_get_config_item(path.dentry);
/*
* A hashed dentry guarantees that neither the item nor the dirent
* have been released yet, as removals unhash before dropping.
* Grab both references here. An item reference alone would not keep
* ->ci_dentry alive.
*/
spin_lock(&path.dentry->d_lock);
if (!d_unhashed(path.dentry)) {
struct configfs_dirent *sd = path.dentry->d_fsdata;
*target = config_item_get(sd->s_element);
*target_sd = configfs_get(sd);
}
spin_unlock(&path.dentry->d_lock);
if (!*target)
return -ENOENT;
return 0;
@@ -139,6 +153,7 @@ int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
struct configfs_dirent *sd;
struct config_item *parent_item;
struct config_item *target_item = NULL;
struct configfs_dirent *target_sd = NULL;
const struct config_item_type *type;
sd = dentry->d_parent->d_fsdata;
@@ -182,7 +197,7 @@ int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
* AV, a thoroughly annoyed bastard.
*/
inode_unlock(dir);
ret = get_target(symname, &target_item, dentry->d_sb);
ret = get_target(symname, &target_item, &target_sd, dentry->d_sb);
inode_lock(dir);
if (ret)
goto out_put;
@@ -196,13 +211,14 @@ int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
ret = type->ct_item_ops->allow_link(parent_item, target_item);
if (!ret) {
mutex_lock(&configfs_symlink_mutex);
ret = create_link(parent_item, target_item, dentry);
ret = create_link(parent_item, target_item, target_sd, dentry);
mutex_unlock(&configfs_symlink_mutex);
if (ret && type->ct_item_ops->drop_link)
type->ct_item_ops->drop_link(parent_item,
target_item);
}
configfs_put(target_sd);
config_item_put(target_item);
out_put:

View File

@@ -196,7 +196,7 @@ static int powerpc_vpadtl_sample(struct powerpc_vpadtl_entry *record,
sample.cpumode = PERF_RECORD_MISC_KERNEL;
sample.time = save;
sample.raw_data = record;
sample.raw_size = sizeof(record);
sample.raw_size = sizeof(*record);
event.sample.header.type = PERF_RECORD_SAMPLE;
event.sample.header.misc = sample.cpumode;
event.sample.header.size = sizeof(struct perf_event_header);

View File

@@ -1947,7 +1947,16 @@ int dso__load(struct dso *dso, struct map *map)
if (next_slot) {
ss_pos++;
if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND)
/*
* The binary type is used to find the file containing
* the executed instructions, so prefer the types that
* refer to the actual object over debug-only files such
* as DSO_BINARY_TYPE__DEBUGLINK.
*/
if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND ||
symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE ||
(symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_DSO &&
dso__binary_type(dso) != DSO_BINARY_TYPE__BUILD_ID_CACHE))
dso__set_binary_type(dso, symtab_type);
if (syms_ss && runtime_ss)