[PATCH] tools/mm: prevent page_owner_sort from truncating input
From: Warren Xiong
Date: Wed Jul 29 2026 - 01:39:01 EST
page_owner_sort opens the output file with "w" before reading the input.
If both paths refer to the same file, this truncates the input and the
tool silently processes zero records before returning success.
Open the output without truncating it and compare the device and inode
numbers of the opened files. Reject matching regular files, then truncate
regular output files only after the check. This also detects hard link and
symbolic link aliases while preserving non-regular outputs such as
/dev/stdout.
Signed-off-by: Warren Xiong <warren.xiong@xxxxxxxxxx>
---
tools/mm/page_owner_sort.c | 41 ++++++++++++++++++++++++++++++++++----
1 file changed, 37 insertions(+), 4 deletions(-)
diff --git a/tools/mm/page_owner_sort.c b/tools/mm/page_owner_sort.c
index 35d3d2549..602f541d8 100644
--- a/tools/mm/page_owner_sort.c
+++ b/tools/mm/page_owner_sort.c
@@ -713,7 +713,8 @@ int main(int argc, char **argv)
FILE *fin, *fout;
char *buf, *ext_buf;
int i, count, compare_flag;
- struct stat st;
+ int fout_fd;
+ struct stat st, output_st;
int opt;
struct option longopts[] = {
{ "pid", required_argument, NULL, 1 },
@@ -836,13 +837,46 @@ int main(int argc, char **argv)
}
fin = fopen(argv[optind], "r");
- fout = fopen(argv[optind + 1], "w");
- if (!fin || !fout) {
+ if (!fin) {
usage();
perror("open: ");
exit(1);
}
+ if (fstat(fileno(fin), &st)) {
+ perror("fstat input");
+ exit(1);
+ }
+
+ /*
+ * Do not truncate the output until after checking whether it refers
+ * to the input file. Comparing the opened files also catches aliases
+ * created with hard links or symbolic links.
+ */
+ fout_fd = open(argv[optind + 1], O_WRONLY | O_CREAT, 0666);
+ if (fout_fd < 0) {
+ perror("open output");
+ exit(1);
+ }
+ if (fstat(fout_fd, &output_st)) {
+ perror("fstat output");
+ exit(1);
+ }
+ if (S_ISREG(st.st_mode) && S_ISREG(output_st.st_mode) &&
+ st.st_dev == output_st.st_dev && st.st_ino == output_st.st_ino) {
+ fprintf(stderr, "Input and output files must be different\n");
+ exit(1);
+ }
+ if (S_ISREG(output_st.st_mode) && ftruncate(fout_fd, 0)) {
+ perror("truncate output");
+ exit(1);
+ }
+ fout = fdopen(fout_fd, "w");
+ if (!fout) {
+ perror("fdopen output");
+ exit(1);
+ }
+
if (!check_regcomp(&order_pattern, "order\\s*([0-9]*),"))
goto out_order;
if (!check_regcomp(&pid_pattern, "pid\\s*([0-9]*),"))
@@ -854,7 +888,6 @@ int main(int argc, char **argv)
if (!check_regcomp(&ts_nsec_pattern, "ts\\s*([0-9]*)\\s*ns"))
goto out_ts;
- fstat(fileno(fin), &st);
max_size = st.st_size / 100; /* hack ... */
list = malloc(max_size * sizeof(*list));
--
2.39.5