1 : /*
2 : * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include <stdio.h>
8 : #include <sys/types.h>
9 : #include <sys/stat.h>
10 : #include <fcntl.h>
11 : #include <errno.h>
12 :
13 : #include "native_client/src/include/portability.h"
14 : #include "native_client/src/include/portability_string.h"
15 : #include "native_client/src/include/nacl_macros.h"
16 :
17 : #include "native_client/src/shared/platform/nacl_check.h"
18 : #include "native_client/src/shared/platform/nacl_semaphore.h"
19 :
20 : #include "native_client/src/shared/platform/nacl_host_desc.h"
21 : #include "native_client/src/shared/platform/nacl_log.h"
22 : #include "native_client/src/shared/platform/nacl_secure_random.h"
23 : #include "native_client/src/shared/platform/nacl_sync.h"
24 : #include "native_client/src/shared/platform/nacl_threads.h"
25 : #include "native_client/src/shared/platform/nacl_time.h"
26 :
27 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
28 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
29 : #include "native_client/src/trusted/desc/nacl_desc_quota.h"
30 : #include "native_client/src/trusted/desc/nacl_desc_quota_interface.h"
31 :
32 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
33 :
34 : #if NACL_WINDOWS
35 : # define UNLINK(f) _unlink(f)
36 : #else
37 : # define UNLINK(f) unlink(f)
38 : #endif
39 :
40 : char *gProgram = NULL;
41 :
42 : uint64_t gNumBytes;
43 :
44 : struct NaClDescFake {
45 : struct NaClDesc base NACL_IS_REFCOUNT_SUBCLASS;
46 : uint64_t bytes_written;
47 : };
48 :
49 : /*
50 : * A fake descriptor quota interface.
51 : */
52 :
53 1 : static void FakeDtor(struct NaClRefCount *nrcp) {
54 1 : nrcp->vtbl = (struct NaClRefCountVtbl *)(&kNaClDescQuotaInterfaceVtbl);
55 1 : (*nrcp->vtbl->Dtor)(nrcp);
56 1 : }
57 :
58 : static int64_t FakeWriteRequest(struct NaClDescQuotaInterface *quota_interface,
59 : uint8_t const *file_id,
60 : int64_t offset,
61 1 : int64_t length) {
62 : UNREFERENCED_PARAMETER(quota_interface);
63 : UNREFERENCED_PARAMETER(file_id);
64 : UNREFERENCED_PARAMETER(offset);
65 :
66 : NaClLog(1,
67 : ("NaClSrpcPepperWriteRequest(dummy): requesting length %"NACL_PRId64
68 : ", (0x%"NACL_PRIx64")\n"),
69 1 : length, length);
70 1 : if (length < 0) {
71 0 : NaClLog(LOG_FATAL, "Negative length: %"NACL_PRId64"\n", length);
72 : }
73 1 : if ((uint64_t) length > gNumBytes) {
74 1 : NaClLog(1, "NaClSrpcPepperWriteRequest(dummy): clamping!\n");
75 1 : length = (int64_t) gNumBytes;
76 : }
77 : /* post: length <= gNumBytes */
78 : NaClLog(1,
79 : ("NaClSrpcPepperWriteRequest(dummy): allowing length %"NACL_PRId64
80 : ", (0x%"NACL_PRIx64")\n"),
81 1 : length, length);
82 1 : gNumBytes -= length;
83 1 : return length;
84 1 : }
85 :
86 : static int64_t FakeFtruncateRequest(
87 : struct NaClDescQuotaInterface *quota_interface,
88 : uint8_t const *file_id,
89 0 : int64_t length) {
90 : UNREFERENCED_PARAMETER(quota_interface);
91 : UNREFERENCED_PARAMETER(file_id);
92 :
93 0 : NaClLog(LOG_FATAL, "FtruncateRequest invoked!?!\n");
94 0 : return length;
95 0 : }
96 :
97 : struct NaClDescQuotaInterfaceVtbl const kFakeQuotaInterfaceVtbl = {
98 : {
99 : FakeDtor
100 : },
101 : FakeWriteRequest,
102 : FakeFtruncateRequest
103 : };
104 :
105 : struct FakeQuotaInterface {
106 : struct NaClDescQuotaInterface base NACL_IS_REFCOUNT_SUBCLASS;
107 : };
108 :
109 1 : int FakeQuotaInterfaceCtor(struct FakeQuotaInterface *self) {
110 1 : struct NaClRefCount *nrcp = (struct NaClRefCount *) self;
111 1 : if (!NaClDescQuotaInterfaceCtor(&(self->base))) {
112 0 : return 0;
113 : }
114 1 : nrcp->vtbl = (struct NaClRefCountVtbl *)(&kFakeQuotaInterfaceVtbl);
115 1 : return 1;
116 1 : }
117 :
118 : int ExerciseQuotaObject(struct NaClDescQuota *test_obj,
119 : struct NaClSecureRng *rngp,
120 : char *buffer,
121 : size_t max_write_size,
122 1 : uint64_t num_bytes) {
123 : nacl_off64_t file_size;
124 :
125 1 : gNumBytes = num_bytes;
126 :
127 : NaClLog(LOG_INFO,
128 : "ExerciseQuotaObject: allow total %"NACL_PRId64" bytes\n",
129 1 : num_bytes);
130 : NaClLog(LOG_INFO,
131 : "ExerciseQuotaObject: random write sizes, up to %"
132 : NACL_PRIdS" bytes\n",
133 1 : max_write_size);
134 :
135 1 : while (gNumBytes > 0) {
136 : size_t write_size;
137 : uint32_t limit;
138 : ssize_t result;
139 : uint64_t old_size;
140 :
141 1 : NACL_COMPILE_TIME_ASSERT(sizeof(uint32_t) <= sizeof(size_t));
142 :
143 : /* following is always false on ILP32 */
144 1 : if (max_write_size > NACL_UMAX_VAL(uint32_t)) {
145 0 : limit = NACL_UMAX_VAL(uint32_t);
146 0 : } else {
147 1 : limit = (uint32_t) max_write_size;
148 : }
149 :
150 1 : write_size = (*rngp->base.vtbl->Uniform)(&rngp->base, limit);
151 :
152 1 : NaClLog(LOG_INFO, "Random size: %"NACL_PRIdS"\n", write_size);
153 :
154 1 : old_size = gNumBytes;
155 :
156 : result = (*NACL_VTBL(NaClDesc, test_obj)->
157 1 : Write)((struct NaClDesc *) test_obj, buffer, write_size);
158 :
159 1 : if (result < 0) {
160 0 : NaClLog(LOG_INFO, "Write result %"NACL_PRIdS"\n", result);
161 0 : return 1;
162 : }
163 : /*
164 : * OS short write? Clamped write amount?
165 : */
166 1 : if ((size_t) result < write_size && (uint64_t) result != gNumBytes) {
167 : NaClLog(LOG_INFO,
168 : ("Short write: asked for %"NACL_PRIdS" (0x%"NACL_PRIxS") bytes,"
169 : " got %"NACL_PRIdS" (0x%"NACL_PRIxS") bytes.\n"),
170 : write_size, write_size,
171 1 : result, result);
172 : }
173 1 : if ((size_t) result > write_size) {
174 : NaClLog(LOG_FATAL,
175 : ("ERROR: LONG write: asked for %"NACL_PRIdS
176 : " (0x%"NACL_PRIxS") bytes,"
177 : " got %"NACL_PRIdS" (0x%"NACL_PRIxS") bytes.\n"),
178 : write_size, write_size,
179 0 : result, result);
180 0 : return 1;
181 : }
182 :
183 1 : if (old_size - gNumBytes != (uint64_t) result) {
184 : NaClLog(LOG_FATAL,
185 0 : "Write tracking failure.\n");
186 : }
187 1 : }
188 :
189 1 : NaClLog(LOG_INFO, "... checking file size\n");
190 :
191 : file_size = (*NACL_VTBL(NaClDesc, test_obj)->
192 : Seek)((struct NaClDesc *) test_obj,
193 : 0,
194 1 : SEEK_END);
195 1 : CHECK((uint64_t) file_size == num_bytes);
196 :
197 1 : NaClLog(LOG_INFO, "OK.\n");
198 :
199 1 : return 0;
200 1 : }
201 :
202 0 : void Usage(void) {
203 0 : fprintf(stderr, "Usage: %s -f file-to-write [-n num_bytes]\n", gProgram);
204 0 : }
205 :
206 1 : int main(int ac, char **av) {
207 1 : int exit_status = 1;
208 1 : int num_errors = 0;
209 : int opt;
210 1 : char *file_path = NULL;
211 : char *buffer;
212 1 : size_t max_write_size = 5UL << 10; /* > 1 page */
213 : size_t ix;
214 1 : uint64_t num_bytes = 16UL << 20;
215 1 : struct NaClDescIoDesc *ndip = NULL;
216 1 : struct NaClDescQuota *object_under_test = NULL;
217 1 : struct FakeQuotaInterface *fake_interface = NULL;
218 : struct NaClSecureRng rng;
219 : static uint8_t file_id0[NACL_DESC_QUOTA_FILE_ID_LEN];
220 : static const char file_id0_cstr[NACL_DESC_QUOTA_FILE_ID_LEN] = "File ID 0";
221 :
222 1 : NaClLogModuleInit();
223 1 : NaClTimeInit();
224 1 : NaClSecureRngModuleInit();
225 :
226 1 : gProgram = strrchr(av[0], '/');
227 1 : if (NULL == gProgram) {
228 1 : gProgram = av[0];
229 1 : } else {
230 0 : ++gProgram;
231 : }
232 :
233 1 : memset(file_id0, 0, sizeof file_id0);
234 1 : memcpy(file_id0, file_id0_cstr, sizeof file_id0_cstr);
235 :
236 1 : while (EOF != (opt = getopt(ac, av, "f:m:n:"))) {
237 1 : switch (opt) {
238 : case 'f':
239 1 : file_path = optarg;
240 1 : break;
241 : case 'm':
242 0 : max_write_size = (size_t) STRTOULL(optarg, (char **) NULL, 0);
243 : case 'n':
244 0 : num_bytes = (uint64_t) STRTOULL(optarg, (char **) NULL, 0);
245 0 : break;
246 : default:
247 0 : Usage();
248 0 : exit_status = 1;
249 0 : goto cleanup;
250 : }
251 1 : }
252 1 : if (NULL == file_path) {
253 0 : fprintf(stderr, "Please provide a path name for a temporary file that\n");
254 0 : fprintf(stderr, "this test can create/overwrite.\n");
255 0 : Usage();
256 0 : exit_status = 2;
257 0 : goto cleanup;
258 : }
259 :
260 : /*
261 : * Invariant in cleanup code: ndip and object_under_test, if
262 : * non-NULL, needs to be Dtor'd (via NaClDescSafeUnref). So:
263 : *
264 : * 1) We never have a partially-constructed object. (Our Ctors fail
265 : * atomically.)
266 : *
267 : * 2) If a Ctor takes ownership of another object, we immediately
268 : * replace our reference to that object with NULL, so that the
269 : * cleanup code would not try to Dtor the contained object
270 : * independently of the containing object.
271 : */
272 : if (NULL == (ndip = NaClDescIoDescOpen(file_path,
273 : NACL_ABI_O_RDWR | NACL_ABI_O_CREAT,
274 1 : 0777))) {
275 0 : fprintf(stderr, "%s could not open file %s\n", gProgram, file_path);
276 0 : exit_status = 3;
277 0 : goto cleanup_file;
278 : }
279 1 : if (NULL == (fake_interface = malloc(sizeof *fake_interface))) {
280 0 : perror(gProgram);
281 0 : fprintf(stderr, "No memory for fake interface.\n");
282 0 : exit_status = 4;
283 0 : goto cleanup_file;
284 : }
285 1 : if (!FakeQuotaInterfaceCtor(fake_interface)) {
286 0 : perror(gProgram);
287 0 : fprintf(stderr, "Ctor for fake quota interface failed.\n");
288 : /*
289 : * fake_interface is not constructed, so we must free it and
290 : * NULL it out to prevent the cleanup code Dtors from firing.
291 : */
292 0 : free(fake_interface);
293 0 : fake_interface = NULL;
294 0 : exit_status = 5;
295 0 : goto cleanup_file;
296 : }
297 1 : if (NULL == (object_under_test = malloc(sizeof *object_under_test))) {
298 0 : perror(gProgram);
299 0 : fprintf(stderr, "No memory for object under test.\n");
300 0 : exit_status = 4;
301 0 : goto cleanup_file;
302 : }
303 : if (!NaClDescQuotaCtor(object_under_test,
304 : (struct NaClDesc *) ndip,
305 : file_id0,
306 1 : (struct NaClDescQuotaInterface *) fake_interface)) {
307 0 : perror(gProgram);
308 0 : fprintf(stderr, "Ctor for quota object failed.\n");
309 : /*
310 : * object_under_test is not constructed, so we must free it and
311 : * NULL it out to prevent the cleanup code Dtors from firing.
312 : */
313 0 : free(object_under_test);
314 0 : object_under_test = NULL;
315 0 : exit_status = 5;
316 0 : goto cleanup_file;
317 : }
318 :
319 1 : ndip = NULL; /* object_under_test has ownership */
320 :
321 1 : if (!NaClSecureRngCtor(&rng)) {
322 0 : fprintf(stderr, "Ctor for SecureRng failed.\n");
323 0 : exit_status = 6;
324 0 : goto cleanup_file;
325 : }
326 :
327 1 : if (NULL == (buffer = malloc(max_write_size))) {
328 0 : fprintf(stderr, "No memory for write source buffer\n");
329 0 : exit_status = 7;
330 0 : goto cleanup_file;
331 : }
332 :
333 1 : for (ix = 0; ix < max_write_size; ++ix) {
334 1 : buffer[ix] = (char) ix;
335 1 : }
336 :
337 : num_errors += ExerciseQuotaObject(object_under_test,
338 : &rng,
339 : buffer,
340 : max_write_size,
341 1 : num_bytes);
342 :
343 1 : if (num_errors > 0) {
344 0 : printf("Total %d errors\n", num_errors);
345 0 : exit_status = 8;
346 0 : } else {
347 1 : printf("PASSED.\n");
348 1 : exit_status = 0;
349 : }
350 :
351 : cleanup_file:
352 : NaClDescQuotaInterfaceSafeUnref(
353 1 : (struct NaClDescQuotaInterface *) fake_interface);
354 1 : NaClDescSafeUnref((struct NaClDesc *) ndip);
355 1 : NaClDescSafeUnref((struct NaClDesc *) object_under_test);
356 :
357 1 : if (-1 == UNLINK(file_path)) {
358 0 : perror("nacl_desc_quota_test");
359 : fprintf(stderr,
360 : "unlink of a temporary file failed during test cleanup, errno %d\n",
361 0 : errno);
362 0 : exit_status = 9;
363 : }
364 :
365 : cleanup:
366 1 : return exit_status;
367 1 : }
|