1 /*
2 * Copyright (C) 2018 MediaTek Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 or any later of the GNU General Public License
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 * Started by Eddie Horng <eddie.horng@mediatek.com>
24 *
25 * DESCRIPTION
26 * Check if an unlinked executable can run in overlayfs mount.
27 * The regression is introduced from 8db6c34f1dbc ("Introduce v3
28 * namespaced file capabilities"). in security/commoncap.c,
29 * cap_inode_getsecurity() use d_find_alias() cause unhashed dentry
30 * can't be found. The solution could use d_find_any_alias() instead of
31 * d_find_alias().
32 *
33 * Starting with kernel 4.14, this case fails, execveat shall
34 * returns EINVAL.
35 *
36 * This has been fixed by:
37 * 355139a8dba4 ("cap_inode_getsecurity: use d_find_any_alias()
38 * instead of d_find_alias()")
39 */
40
41 #define _GNU_SOURCE
42 #include "config.h"
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <errno.h>
49 #include <string.h>
50 #include <sys/syscall.h>
51 #include <sys/mount.h>
52 #include <fcntl.h>
53 #include "tst_test.h"
54 #include "lapi/execveat.h"
55 #include "lapi/fcntl.h"
56 #include "execveat.h"
57
58 #define OVL_MNT "ovl"
59 #define TEST_APP "execveat_child"
60 #define TEST_FILE_PATH OVL_MNT"/"TEST_APP
61
62 static int ovl_mounted;
63
do_child(void)64 static void do_child(void)
65 {
66 char *argv[2] = {TEST_FILE_PATH, NULL};
67 int fd;
68
69 SAFE_CP(TEST_APP, TEST_FILE_PATH);
70
71 fd = SAFE_OPEN(TEST_FILE_PATH, O_PATH);
72 SAFE_UNLINK(TEST_FILE_PATH);
73
74 TEST(execveat(fd, "", argv, environ, AT_EMPTY_PATH));
75 tst_res(TFAIL | TERRNO, "execveat() returned unexpected errno");
76 }
77
verify_execveat(void)78 static void verify_execveat(void)
79 {
80 pid_t pid;
81
82 pid = SAFE_FORK();
83 if (pid == 0)
84 do_child();
85 }
86
setup(void)87 static void setup(void)
88 {
89 int ret;
90
91 check_execveat();
92
93 /* Setup an overlay mount with lower file */
94 SAFE_MKDIR("lower", 0755);
95 SAFE_MKDIR("upper", 0755);
96 SAFE_MKDIR("work", 0755);
97 SAFE_MKDIR(OVL_MNT, 0755);
98 ret = mount("overlay", OVL_MNT, "overlay", 0,
99 "lowerdir=lower,upperdir=upper,workdir=work");
100 if (ret < 0) {
101 if (errno == ENODEV) {
102 tst_brk(TCONF,
103 "overlayfs is not configured in this kernel.");
104 }
105 tst_brk(TBROK | TERRNO, "overlayfs mount failed");
106 }
107 ovl_mounted = 1;
108 }
109
cleanup(void)110 static void cleanup(void)
111 {
112 if (ovl_mounted)
113 SAFE_UMOUNT(OVL_MNT);
114 }
115
116 static const char *const resource_files[] = {
117 TEST_APP,
118 NULL,
119 };
120
121 static struct tst_test test = {
122 .needs_root = 1,
123 .needs_tmpdir = 1,
124 .forks_child = 1,
125 .child_needs_reinit = 1,
126 .setup = setup,
127 .cleanup = cleanup,
128 .test_all = verify_execveat,
129 .resource_files = resource_files,
130 };
131