Skip to content

Commit 0368582

Browse files
Abhishekmishra2808xiaoxiang781216
authored andcommitted
fs/binfmt: Enforce POSIX execute permissions prior to binary load
Adds a pre-load permission check in exec_internal() to verify the calling task has the required execute (x) bits for the target file. Properly evaluates root (euid == 0), owner, group, and other permissions. This ensures POSIX compliance and cleanly rejects unauthorized files with -EACCES before they reach the ELF loader, preventing unnecessary memory allocation and downstream hardware execution faults. Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
1 parent 84aba62 commit 0368582

9 files changed

Lines changed: 199 additions & 16 deletions

File tree

binfmt/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ list(
4141
binfmt_copyactions.c
4242
binfmt_dumpmodule.c)
4343

44+
if(CONFIG_SCHED_USER_IDENTITY)
45+
list(APPEND SRCS binfmt_checkexec.c)
46+
endif()
47+
4448
if(CONFIG_BINFMT_LOADABLE)
4549
list(APPEND SRCS binfmt_exit.c)
4650
endif()

binfmt/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ CSRCS = binfmt_globals.c binfmt_initialize.c binfmt_register.c binfmt_unregiste
2828
CSRCS += binfmt_loadmodule.c binfmt_unloadmodule.c binfmt_execmodule.c
2929
CSRCS += binfmt_exec.c binfmt_copyargv.c binfmt_copyactions.c binfmt_dumpmodule.c
3030

31+
ifeq ($(CONFIG_SCHED_USER_IDENTITY),y)
32+
CSRCS += binfmt_checkexec.c
33+
endif
34+
3135
ifeq ($(CONFIG_BINFMT_LOADABLE),y)
3236
CSRCS += binfmt_exit.c
3337
endif

binfmt/binfmt.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,26 @@ void binfmt_freeactions(FAR const posix_spawn_file_actions_t *copy);
213213
# define binfmt_freeactions(copy)
214214
#endif
215215

216+
#ifdef CONFIG_SCHED_USER_IDENTITY
217+
/****************************************************************************
218+
* Name: binfmt_checkexecperm
219+
*
220+
* Description:
221+
* Verify that the calling task has execute permission on the file
222+
* described by 'bin'. The file owner, group, and mode must already be
223+
* populated in the binary_s structure.
224+
*
225+
* Input Parameters:
226+
* bin - Load structure with uid, gid, and mode populated
227+
*
228+
* Returned Value:
229+
* Zero (OK) on success; -EACCES if execute permission is denied.
230+
*
231+
****************************************************************************/
232+
233+
int binfmt_checkexecperm(FAR struct binary_s *bin);
234+
#endif
235+
216236
#ifdef CONFIG_BUILTIN
217237
/****************************************************************************
218238
* Name: builtin_initialize

binfmt/binfmt_checkexec.c

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/****************************************************************************
2+
* binfmt/binfmt_checkexec.c
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Licensed to the Apache Software Foundation (ASF) under one or more
7+
* contributor license agreements. See the NOTICE file distributed with
8+
* this work for additional information regarding copyright ownership. The
9+
* ASF licenses this file to you under the Apache License, Version 2.0 (the
10+
* "License"); you may not use this file except in compliance with the
11+
* License. You may obtain a copy of the License at
12+
*
13+
* http://www.apache.org/licenses/LICENSE-2.0
14+
*
15+
* Unless required by applicable law or agreed to in writing, software
16+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18+
* License for the specific language governing permissions and limitations
19+
* under the License.
20+
*
21+
****************************************************************************/
22+
23+
/****************************************************************************
24+
* Included Files
25+
****************************************************************************/
26+
27+
#include <nuttx/config.h>
28+
29+
#include <sys/stat.h>
30+
#include <errno.h>
31+
32+
#include <nuttx/sched.h>
33+
#include <nuttx/binfmt/binfmt.h>
34+
35+
#include "binfmt.h"
36+
37+
/****************************************************************************
38+
* Public Functions
39+
****************************************************************************/
40+
41+
/****************************************************************************
42+
* Name: binfmt_checkexecperm
43+
*
44+
* Description:
45+
* Verify that the calling task has execute permission on the file
46+
* described by 'bin'. The file owner, group, and mode must already be
47+
* populated in the binary_s structure before calling this function.
48+
*
49+
* Input Parameters:
50+
* bin - Pointer to the binary descriptor with uid, gid, and mode set.
51+
*
52+
* Returned Value:
53+
* Zero (OK) on success; -EACCES if execute permission is denied.
54+
*
55+
****************************************************************************/
56+
57+
int binfmt_checkexecperm(FAR struct binary_s *bin)
58+
{
59+
FAR struct tcb_s *rtcb;
60+
mode_t xbits;
61+
62+
rtcb = nxsched_self();
63+
64+
if (bin == NULL || rtcb == NULL || rtcb->group == NULL)
65+
{
66+
return OK;
67+
}
68+
69+
if (rtcb->group->tg_euid == 0)
70+
{
71+
/* Root can execute any file that has at least one execute bit set */
72+
73+
if ((bin->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
74+
{
75+
return -EACCES;
76+
}
77+
78+
return OK;
79+
}
80+
81+
if (rtcb->group->tg_euid == bin->uid)
82+
{
83+
xbits = S_IXUSR;
84+
}
85+
else if (rtcb->group->tg_egid == bin->gid)
86+
{
87+
xbits = S_IXGRP;
88+
}
89+
else
90+
{
91+
xbits = S_IXOTH;
92+
}
93+
94+
if ((bin->mode & xbits) == 0)
95+
{
96+
return -EACCES;
97+
}
98+
99+
return OK;
100+
}

binfmt/binfmt_exec.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
#include <errno.h>
3232

3333
#include <nuttx/kmalloc.h>
34-
#include <nuttx/sched.h>
3534
#include <nuttx/binfmt/binfmt.h>
3635

3736
#include "binfmt.h"
@@ -81,11 +80,13 @@ static int exec_internal(FAR const char *filename,
8180
FAR const posix_spawnattr_t *attr, bool spawn)
8281
{
8382
FAR struct binary_s *bin;
84-
int pid;
85-
int ret;
8683
#ifndef CONFIG_BINFMT_LOADABLE
8784
struct binary_s sbin;
85+
#endif
86+
int pid;
87+
int ret;
8888

89+
#ifndef CONFIG_BINFMT_LOADABLE
8990
bin = &sbin;
9091
memset(bin, 0, sizeof(*bin));
9192
#else

binfmt/binfmt_loadmodule.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ static int load_absmodule(FAR struct binary_s *bin, FAR const char *filename,
134134
binfmt_dumpmodule(bin);
135135
break;
136136
}
137+
else if (ret == -EACCES)
138+
{
139+
/* Access explicitly denied -- stop here; do not let a fallback
140+
* loader bypass the execute-permission check that already ran.
141+
*/
142+
143+
break;
144+
}
137145
}
138146

139147
return ret;

binfmt/builtin.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
#include <nuttx/binfmt/binfmt.h>
3535
#include <nuttx/lib/builtin.h>
3636

37+
#include "binfmt.h"
38+
3739
#ifdef CONFIG_BUILTIN
3840

3941
/****************************************************************************
@@ -76,6 +78,9 @@ static int builtin_loadbinary(FAR struct binary_s *binp,
7678
FAR const struct builtin_s *builtin;
7779
FAR char *name;
7880
int index;
81+
#ifdef CONFIG_SCHED_USER_IDENTITY
82+
int chk;
83+
#endif
7984

8085
binfo("Loading file: %s\n", filename);
8186

@@ -105,14 +110,21 @@ static int builtin_loadbinary(FAR struct binary_s *binp,
105110
return -ENOENT;
106111
}
107112

113+
#ifdef CONFIG_SCHED_USER_IDENTITY
114+
binp->uid = builtin->uid;
115+
binp->gid = builtin->gid;
116+
binp->mode = builtin->mode;
117+
118+
chk = binfmt_checkexecperm(binp);
119+
if (chk < 0)
120+
{
121+
return chk;
122+
}
123+
#endif
124+
108125
binp->entrypt = builtin->main;
109126
binp->stacksize = builtin->stacksize;
110127
binp->priority = builtin->priority;
111-
#ifdef CONFIG_SCHED_USER_IDENTITY
112-
binp->uid = builtin->uid;
113-
binp->gid = builtin->gid;
114-
binp->mode = builtin->mode;
115-
#endif
116128

117129
return OK;
118130
}

binfmt/elf.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
#include <nuttx/binfmt/binfmt.h>
3838
#include <nuttx/kmalloc.h>
3939

40+
#include "binfmt.h"
41+
4042
#ifdef CONFIG_ELF
4143

4244
/****************************************************************************
@@ -110,6 +112,20 @@ static int elf_loadbinary(FAR struct binary_s *binp,
110112
goto errout_with_init;
111113
}
112114

115+
#ifdef CONFIG_SCHED_USER_IDENTITY
116+
/* Save IDs and mode from file system before loading segments */
117+
118+
binp->uid = loadinfo.fileuid;
119+
binp->gid = loadinfo.filegid;
120+
binp->mode = loadinfo.filemode;
121+
122+
ret = binfmt_checkexecperm(binp);
123+
if (ret < 0)
124+
{
125+
goto errout_with_init;
126+
}
127+
#endif
128+
113129
/* Load the program binary */
114130

115131
ret = libelf_load_with_addrenv(&loadinfo);
@@ -201,14 +217,6 @@ static int elf_loadbinary(FAR struct binary_s *binp,
201217
binp->mod.nfini = loadinfo.nfini;
202218
#endif
203219

204-
#ifdef CONFIG_SCHED_USER_IDENTITY
205-
/* Save IDs and mode from file system */
206-
207-
binp->uid = loadinfo.fileuid;
208-
binp->gid = loadinfo.filegid;
209-
binp->mode = loadinfo.filemode;
210-
#endif
211-
212220
libelf_dumpentrypt(&loadinfo);
213221
#ifdef CONFIG_PIC
214222
if (loadinfo.gotindex >= 0)

binfmt/nxflat.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <nuttx/config.h>
2828

2929
#include <sys/param.h>
30+
#include <sys/stat.h>
3031
#include <sys/types.h>
3132
#include <stdint.h>
3233
#include <string.h>
@@ -36,10 +37,13 @@
3637

3738
#include <arpa/inet.h>
3839

40+
#include <nuttx/fs/fs.h>
3941
#include <nuttx/kmalloc.h>
4042
#include <nuttx/binfmt/binfmt.h>
4143
#include <nuttx/binfmt/nxflat.h>
4244

45+
#include "binfmt.h"
46+
4347
#ifdef CONFIG_NXFLAT
4448

4549
/****************************************************************************
@@ -142,6 +146,9 @@ static int nxflat_loadbinary(FAR struct binary_s *binp,
142146
int nexports)
143147
{
144148
struct nxflat_loadinfo_s loadinfo; /* Contains globals for libnxflat */
149+
#ifdef CONFIG_SCHED_USER_IDENTITY
150+
struct stat st;
151+
#endif
145152
int ret;
146153

147154
binfo("Loading file: %s\n", filename);
@@ -156,6 +163,25 @@ static int nxflat_loadbinary(FAR struct binary_s *binp,
156163
goto errout;
157164
}
158165

166+
#ifdef CONFIG_SCHED_USER_IDENTITY
167+
ret = file_fstat(&loadinfo.file, &st);
168+
if (ret < 0)
169+
{
170+
berr("Failed to stat NXFLAT program binary: %d\n", ret);
171+
goto errout_with_init;
172+
}
173+
174+
binp->uid = st.st_uid;
175+
binp->gid = st.st_gid;
176+
binp->mode = st.st_mode;
177+
178+
ret = binfmt_checkexecperm(binp);
179+
if (ret < 0)
180+
{
181+
goto errout_with_init;
182+
}
183+
#endif
184+
159185
/* Load the program binary */
160186

161187
ret = nxflat_load(&loadinfo);

0 commit comments

Comments
 (0)