From eeb42cef6bea9bd5d6f657b8c58e41b4b5371d21 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Sun, 31 Aug 2025 10:03:03 +0900 Subject: [PATCH] lsfd: refer to /proc/$pid/map_files if a mapped file is masked To extract file information, lsfd called stat(2) on the the file name found in /proc/$pid/maps. However, lsfd may retrieve information for the wrong file if the file is bind-mount'ed after the process maps the file. To obtain the information about the originally mapped file, this change makes lsfd call fstat(2) on the corresponding entry under /proc/$pid/map_files avoiding the bind-mount ambiguity. Signed-off-by: Masatake YAMATO --- lsfd-cmd/lsfd.c | 6 ++ tests/expected/lsfd/mkfds-mmap-masked-file | 4 + tests/ts/lsfd/mkfds-mmap-masked-file | 88 ++++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 tests/expected/lsfd/mkfds-mmap-masked-file create mode 100755 tests/ts/lsfd/mkfds-mmap-masked-file diff --git a/lsfd-cmd/lsfd.c b/lsfd-cmd/lsfd.c index c17209896..4596697ad 100644 --- a/lsfd-cmd/lsfd.c +++ b/lsfd-cmd/lsfd.c @@ -1017,6 +1017,12 @@ static void parse_maps_line(struct path_cxt *pc, char *buf, struct proc *proc) * "stat by the file name" may not work. In that case, */ goto try_map_files; + if (sb.st_ino != ino || sb.st_dev != devno) + /* There are two files having the same absolute file names! + * + * Maybe the file is bind-mount'ed after mapped. + */ + goto try_map_files; f = new_file(proc, stat2class(&sb), &sb, path, -assoc); } else { /* As used in tcpdump, AF_PACKET socket can be mmap'ed. */ diff --git a/tests/expected/lsfd/mkfds-mmap-masked-file b/tests/expected/lsfd/mkfds-mmap-masked-file new file mode 100644 index 000000000..608269390 --- /dev/null +++ b/tests/expected/lsfd/mkfds-mmap-masked-file @@ -0,0 +1,4 @@ +INODE: 0 +BEFORE: 0 +INODE: 0 +AFTER: 0 diff --git a/tests/ts/lsfd/mkfds-mmap-masked-file b/tests/ts/lsfd/mkfds-mmap-masked-file new file mode 100755 index 000000000..032c8443d --- /dev/null +++ b/tests/ts/lsfd/mkfds-mmap-masked-file @@ -0,0 +1,88 @@ +#!/bin/bash +# +# Copyright (C) 2025 Masatake YAMATO +# +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +TS_TOPDIR="${0%/*}/../.." +TS_DESC="mmap its backing file is masked by bind mount" + +. "$TS_TOPDIR"/functions.sh +ts_init "$*" +ts_skip_nonroot + +ts_check_test_command "$TS_CMD_LSFD" +ts_check_test_command "$TS_HELPER_MKFDS" +ts_check_test_command "$TS_CMD_MOUNT" +ts_check_test_command "$TS_CMD_UMOUNT" +ts_check_test_command "$TS_CMD_MOUNTPOINT" +ts_check_test_command "$TS_CMD_FALLOCATE" + +ts_check_prog "rm" +ts_check_prog "stat" + +ts_cd "$TS_OUTDIR" + +FILE_MASKED=masked-$$ +FILE_MASKED_INO= +FILE_MASKING=masking-$$ +FILE_MASKING_INO= + +cleanup() +{ + "$TS_CMD_UMOUNT" -q ${FILE_MASKED} + rm -f ${FILE_MASKED} + rm -f ${FILE_MASKING} +} + +trap cleanup EXIT + +if ! "$TS_CMD_FALLOCATE" -l 4096 ${FILE_MASKED}; then + ts_skip "failed to make a file to be masked" +fi +FILE_MASKED_INO=$(stat -c '%i' ${FILE_MASKED}) + +if ! "$TS_CMD_FALLOCATE" -l 4096 ${FILE_MASKING}; then + ts_skip "failed to make a file for masking" +fi +FILE_MASKING_INO=$(stat -c '%i' ${FILE_MASKING}) + +PID= + +{ + coproc MKFDS { "$TS_HELPER_MKFDS" mmap file=${FILE_MASKED}; } +} >> "$TS_OUTPUT" 2>&1 + +if read -u ${MKFDS[0]} PID; then + { + INO_BEFORE=$(${TS_CMD_LSFD} -p "$PID" -n -o INODE -Q 'NAME =~ ".*'${FILE_MASKED}'"') + echo "INODE:" $? + [[ "${FILE_MASKED_INO}" == "${INO_BEFORE}" ]] + echo "BEFORE:" $? + } >> "$TS_OUTPUT" 2>&1 + + if ! "$TS_CMD_MOUNT" --bind ${FILE_MASKING} ${FILE_MASKED}; then + ts_skip "failed to mask ${FILE_MASKED}" + fi + + { + INO_AFTER=$(${TS_CMD_LSFD} -p "$PID" -n -o INODE -Q 'NAME =~ ".*'${FILE_MASKED}'"') + echo "INODE:" $? + [[ "${FILE_MASKED_INO}" == "${INO_AFTER}" ]] + echo "AFTER:" $? + } >> "$TS_OUTPUT" 2>&1 +fi + +echo DONE >&"${MKFDS[1]}" +wait "${MKFDS_PID}" +ts_finalize -- 2.47.3