1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <libmcpu.h>
#include <sys/asm/errno.h>
#include <sys/asm/fcntl.h>
#include <sys/asm/syscall.h>
#include "mcpu-asm.h"
#define CHECK(cond,msg) \
do { \
if( !(cond) ) \
{ \
fprintf( stderr, "FAIL: %s\n", (msg) ); \
return( 1 ); \
} \
} while( 0 )
static void
put32( __mpu_uint32_t address, __mpu_uint32_t value )
{
__cpu_data_put_u32( __cpu_reg_ptr( 32, address ), value );
}
static void
put64( __mpu_uint32_t address, __mpu_uint64_t value )
{
__cpu_data_put_u64( __cpu_reg_ptr( 64, address ), value );
}
static __mpu_uint64_t
reg64( __mpu_uint32_t address )
{
return( __cpu_data_get_u64( __cpu_reg_ptr( 64, address ) ) );
}
static int
run_syscall( __mpu_uint32_t nargs, __mpu_uint32_t sysno,
__mpu_uint64_t arg0, __mpu_uint64_t arg1,
__mpu_uint64_t arg2, __mpu_uint64_t *ret )
{
int rc;
put32( CPU_SYSCALL_NARGS_ADDR, nargs );
put32( CPU_SYSCALL_SYSNO_ADDR, sysno );
put64( CPU_SYSCALL_ARG0_ADDR, arg0 );
put64( CPU_SYSCALL_ARG1_ADDR, arg1 );
put64( CPU_SYSCALL_ARG2_ADDR, arg2 );
__cpu_pc = 0;
rc = __cpu_run();
if( rc != CPU_EXEC_SUCCESS ) return( rc );
*ret = reg64( CPU_SYSCALL_RET_ADDR );
return( 0 );
} /* End of run_syscall() */
static __mpu_int64_t
signed_result( __mpu_uint64_t value )
{
__mpu_int64_t ret;
memcpy( &ret, &value, sizeof( ret ) );
return( ret );
} /* End of signed_result() */
int
main( void )
{
static const char source[] =
".text\n"
"SYSCALL\n"
"HALT\n";
cpu_asm_image image;
char err[256];
__mpu_uint64_t ret;
memset( &image, 0, sizeof( image ) );
CHECK( __NR_dup == 0x126, "public __NR_dup ABI number" );
CHECK( __NR_dup2 == 0x127, "public __NR_dup2 ABI number" );
__cpu_init();
CHECK( cpu_assemble_text( source, &image, err, sizeof( err ) ) == 0, err );
CHECK( __cpu_program_set( image.code, image.code_size ) == 0,
"program load" );
/* dup() returns the lowest free guest descriptor. */
CHECK( run_syscall( 1, __NR_dup, 1, 0, 0, &ret ) == 0, "dup stdout" );
CHECK( ret == 3, "dup returns lowest free guest fd 3" );
/* Descriptor flags are not copied by dup(). */
CHECK( run_syscall( 3, __NR_fcntl, 1, F_SETFD, FD_CLOEXEC, &ret ) == 0,
"set FD_CLOEXEC on source" );
CHECK( ret == 0, "F_SETFD source result" );
CHECK( run_syscall( 3, __NR_fcntl, 3, F_GETFD, 0, &ret ) == 0,
"F_GETFD duplicated descriptor" );
CHECK( ret == 0, "dup clears descriptor flags" );
/* dup2() installs exactly the requested guest descriptor. */
CHECK( run_syscall( 2, __NR_dup2, 1, 10, 0, &ret ) == 0,
"dup2 stdout to guest fd 10" );
CHECK( ret == 10, "dup2 returns requested guest fd" );
CHECK( run_syscall( 3, __NR_fcntl, 10, F_SETFD, FD_CLOEXEC, &ret ) == 0,
"set FD_CLOEXEC on dup2 target" );
CHECK( ret == 0, "set target flags" );
/* Replacing an occupied target clears descriptor flags. */
CHECK( run_syscall( 2, __NR_dup2, 3, 10, 0, &ret ) == 0,
"dup2 replaces occupied target" );
CHECK( ret == 10, "dup2 replacement return" );
CHECK( run_syscall( 3, __NR_fcntl, 10, F_GETFD, 0, &ret ) == 0,
"F_GETFD replaced target" );
CHECK( ret == 0, "dup2 replacement clears descriptor flags" );
/* oldfd == newfd is a no-op and preserves descriptor flags. */
CHECK( run_syscall( 3, __NR_fcntl, 1, F_GETFD, 0, &ret ) == 0,
"F_GETFD source before dup2 self" );
CHECK( ret == FD_CLOEXEC, "source flag before dup2 self" );
CHECK( run_syscall( 2, __NR_dup2, 1, 1, 0, &ret ) == 0,
"dup2 oldfd equals newfd" );
CHECK( ret == 1, "dup2 self return" );
CHECK( run_syscall( 3, __NR_fcntl, 1, F_GETFD, 0, &ret ) == 0,
"F_GETFD source after dup2 self" );
CHECK( ret == FD_CLOEXEC, "dup2 self preserves descriptor flags" );
/* Closing a borrowed standard guest fd does not close HOST stdio, and dup()
may subsequently reuse that lowest free guest slot. */
CHECK( run_syscall( 1, __NR_close, 0, 0, 0, &ret ) == 0,
"close borrowed guest stdin" );
CHECK( ret == 0, "close borrowed guest stdin result" );
CHECK( run_syscall( 1, __NR_dup, 1, 0, 0, &ret ) == 0,
"dup reuses closed guest fd 0" );
CHECK( ret == 0, "dup returns lowest free guest fd 0" );
/* Invalid source and target descriptors use the stable MCPU EBADF value. */
CHECK( run_syscall( 1, __NR_dup, 255, 0, 0, &ret ) == 0,
"dup invalid source" );
CHECK( signed_result( ret ) == -(__mpu_int64_t)EBADF,
"dup invalid source returns -EBADF" );
CHECK( run_syscall( 2, __NR_dup2, 1, 256, 0, &ret ) == 0,
"dup2 invalid target" );
CHECK( signed_result( ret ) == -(__mpu_int64_t)EBADF,
"dup2 invalid target returns -EBADF" );
CHECK( run_syscall( 3, __NR_fcntl, 10, F_SETFD, FD_CLOEXEC, &ret ) == 0,
"set target flag before invalid-source dup2" );
CHECK( run_syscall( 2, __NR_dup2, 255, 10, 0, &ret ) == 0,
"dup2 invalid source with occupied target" );
CHECK( signed_result( ret ) == -(__mpu_int64_t)EBADF,
"dup2 invalid source returns -EBADF" );
CHECK( run_syscall( 3, __NR_fcntl, 10, F_GETFD, 0, &ret ) == 0,
"target survives invalid-source dup2" );
CHECK( ret == FD_CLOEXEC,
"invalid-source dup2 leaves target unchanged" );
CHECK( run_syscall( 1, __NR_close, 0, 0, 0, &ret ) == 0,
"close reused guest fd 0" );
CHECK( run_syscall( 1, __NR_close, 3, 0, 0, &ret ) == 0,
"close dup guest fd 3" );
CHECK( run_syscall( 1, __NR_close, 10, 0, 0, &ret ) == 0,
"close dup2 guest fd 10" );
cpu_asm_image_free( &image );
__cpu_free_context();
puts( "libmcpu dup/dup2 regression: PASS" );
return( 0 );
} /* End of main() */
|