1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <errno.h> 6 #include <fcntl.h> 7 #include <string.h> 8 #include <sys/ioctl.h> 9 #include <sys/select.h> 10 #include <sys/stat.h> 11 #include <sys/time.h> 12 #include <string> 13 14 #include "dev_fs_for_testing.h" 15 #include "fake_ppapi/fake_messaging_interface.h" 16 #include "gtest/gtest.h" 17 #include "nacl_io/devfs/dev_fs.h" 18 #include "nacl_io/filesystem.h" 19 #include "nacl_io/ioctl.h" 20 #include "nacl_io/kernel_intercept.h" 21 #include "nacl_io/kernel_proxy.h" 22 #include "nacl_io/osdirent.h" 23 24 using namespace nacl_io; 25 26 namespace { 27 28 // Helper function for calling ki_ioctl without having 29 // to construct a va_list. 30 int ki_ioctl_wrapper(int fd, int request, ...) { 31 va_list ap; 32 va_start(ap, request); 33 int rtn = ki_ioctl(fd, request, ap); 34 va_end(ap); 35 return rtn; 36 } 37 38 // Helper function for converting PP_Var to C++ string 39 std::string VarToString(VarInterface* var_iface, PP_Var var) { 40 EXPECT_EQ(PP_VARTYPE_STRING, var.type); 41 uint32_t len = 0; 42 const char* str = var_iface->VarToUtf8(var, &len); 43 return std::string(str, len); 44 } 45 46 PP_Var VarFromCStr(VarInterface* iface, const char* string) { 47 return iface->VarFromUtf8(string, strlen(string)); 48 } 49 50 // Helper function for creating message in the format expected by jspipe 51 // nodes: [ name, payload ] 52 PP_Var CreatePipeMessage(PepperInterface* ppapi, const char* pipe, 53 const char* operation, PP_Var payload) { 54 VarInterface* var_iface = ppapi->GetVarInterface(); 55 VarDictionaryInterface* dict_iface = ppapi->GetVarDictionaryInterface(); 56 57 // Create a two element array containing the name of the message 58 // as the first element. Its up to the caller the then set the 59 // second array element. 60 PP_Var message = dict_iface->Create(); 61 PP_Var pipe_var = VarFromCStr(var_iface, pipe); 62 PP_Var operation_var = VarFromCStr(var_iface, operation); 63 PP_Var pipe_key = VarFromCStr(var_iface, "pipe"); 64 PP_Var payload_key = VarFromCStr(var_iface, "payload"); 65 PP_Var operation_key = VarFromCStr(var_iface, "operation"); 66 dict_iface->Set(message, pipe_key, pipe_var); 67 dict_iface->Set(message, operation_key, operation_var); 68 dict_iface->Set(message, payload_key, payload); 69 var_iface->Release(pipe_var); 70 var_iface->Release(operation_var); 71 var_iface->Release(payload); 72 var_iface->Release(pipe_key); 73 var_iface->Release(payload_key); 74 var_iface->Release(operation_key); 75 return message; 76 } 77 78 // Helper function for creating "ack" message in format expected 79 // by jspipe nodes. 80 PP_Var CreateAckMessage(PepperInterface* ppapi, const char* pipe, 81 int32_t count) { 82 return CreatePipeMessage(ppapi, pipe, "ack", PP_MakeInt32(count)); 83 } 84 85 // Helper function for creating "write" message in format expected 86 // by jspipe nodes. 87 PP_Var CreateWriteMessage(PepperInterface* ppapi, 88 const char* pipe, 89 const char* string, 90 int length=-1) { 91 VarArrayBufferInterface* buffer_iface = ppapi->GetVarArrayBufferInterface(); 92 93 if (length == -1) 94 length = strlen(string); 95 96 PP_Var buffer = buffer_iface->Create(length); 97 memcpy(buffer_iface->Map(buffer), string, length); 98 buffer_iface->Unmap(buffer); 99 100 return CreatePipeMessage(ppapi, pipe, "write", buffer); 101 } 102 103 class JSPipeTest : public ::testing::Test { 104 public: 105 void SetUp() { 106 ASSERT_EQ(0, ki_push_state_for_testing()); 107 ASSERT_EQ(0, ki_init_interface(&kp_, &ppapi_)); 108 } 109 110 void TearDown() { 111 ki_uninit(); 112 } 113 114 protected: 115 FakePepperInterface ppapi_; 116 KernelProxy kp_; 117 }; 118 119 class JSPipeNodeTest : public ::testing::Test { 120 public: 121 JSPipeNodeTest() : fs_(&ppapi_) {} 122 123 void SetUp() { 124 name_ = "jspipe1"; 125 ASSERT_EQ(0, fs_.Open(Path("/jspipe1"), O_RDWR, &pipe_dev_)); 126 ASSERT_NE(NULL_NODE, pipe_dev_.get()); 127 struct stat buf; 128 ASSERT_EQ(0, pipe_dev_->GetStat(&buf)); 129 ASSERT_EQ(S_IRUSR | S_IWUSR, buf.st_mode & S_IRWXU); 130 } 131 132 /** 133 * Create a PP_Var message in the same way that we expect 134 * JavaScript code to, and send it to the pipe using ioctl() 135 */ 136 int JSPipeInject(const char* string, int length=-1) { 137 PP_Var message = CreateWriteMessage(&ppapi_, name_, string, length); 138 139 // Send the message via ioctl 140 int rtn = pipe_dev_->Ioctl(NACL_IOC_HANDLEMESSAGE, &message); 141 142 // Release message 143 ppapi_.GetVarInterface()->Release(message); 144 return rtn; 145 } 146 147 int JSPipeInjectAck(int32_t count) { 148 PP_Var message = CreateAckMessage(&ppapi_, name_, count); 149 150 // Send the message via ioctl 151 int rtn = pipe_dev_->Ioctl(NACL_IOC_HANDLEMESSAGE, &message); 152 153 // Release message 154 ppapi_.GetVarInterface()->Release(message); 155 return rtn; 156 } 157 158 // Verify the contents of the jspipe mesage, which should be 159 // { 160 // "pipe": '<pipe_name>', 161 // "operation": '<command_name>', 162 // "payload": payload 163 // } 164 void VerifyPipeMessage(PP_Var message, 165 const char* pipe_name, 166 const char* operation, 167 const char* payload, 168 int payload_length, 169 int32_t int_payload=0) { 170 VarArrayInterface* array_iface = ppapi_.GetVarArrayInterface(); 171 VarDictionaryInterface* dict_iface = ppapi_.GetVarDictionaryInterface(); 172 VarInterface* var_iface = ppapi_.GetVarInterface(); 173 VarArrayBufferInterface* buffer_iface = ppapi_.GetVarArrayBufferInterface(); 174 175 // Verify we have a dictionary with 3 keys 176 ASSERT_EQ(PP_VARTYPE_DICTIONARY, message.type); 177 PP_Var keys = dict_iface->GetKeys(message); 178 ASSERT_EQ(PP_VARTYPE_ARRAY, keys.type); 179 ASSERT_EQ(3, array_iface->GetLength(keys)); 180 var_iface->Release(keys); 181 182 // Verify the keys 183 PP_Var key1 = VarFromCStr(var_iface, "pipe"); 184 PP_Var key2 = VarFromCStr(var_iface, "operation"); 185 PP_Var key3 = VarFromCStr(var_iface, "payload"); 186 187 // Verify pipe name and operation values 188 PP_Var value1 = dict_iface->Get(message, key1); 189 ASSERT_STREQ(pipe_name, VarToString(var_iface, value1).c_str()); 190 var_iface->Release(value1); 191 var_iface->Release(key1); 192 193 PP_Var value2 = dict_iface->Get(message, key2); 194 ASSERT_STREQ(operation, VarToString(var_iface, value2).c_str()); 195 var_iface->Release(value2); 196 var_iface->Release(key2); 197 198 // Verify the payload 199 PP_Var payload_var = dict_iface->Get(message, key3); 200 if (payload != NULL) { 201 ASSERT_EQ(PP_VARTYPE_ARRAY_BUFFER, payload_var.type); 202 ASSERT_EQ(0, memcmp(payload, buffer_iface->Map(payload_var), 203 payload_length)); 204 } else { 205 ASSERT_EQ(PP_VARTYPE_INT32, payload_var.type); 206 ASSERT_EQ(int_payload, payload_var.value.as_int); 207 } 208 var_iface->Release(key3); 209 var_iface->Release(payload_var); 210 } 211 212 protected: 213 FakePepperInterface ppapi_; 214 DevFsForTesting fs_; 215 ScopedNode pipe_dev_; 216 const char* name_; 217 }; 218 219 TEST(JSPipeTestBasic, MissingPepper) { 220 // Create a devfs filesystem without giving it any Pepper implemenation. 221 TypedFsFactory<DevFs> factory; 222 ScopedFilesystem fs; 223 FsInitArgs args(1); 224 factory.CreateFilesystem(args, &fs); 225 ScopedNode pipe_dev; 226 ASSERT_EQ(0, fs->Open(Path("/jspipe1"), O_RDWR, &pipe_dev)); 227 228 // Writing to a pipe should return EIO because Pepper is missing. 229 HandleAttr attrs; 230 int written = -1; 231 ASSERT_EQ(EIO, pipe_dev->Write(attrs, "test", 4, &written)); 232 } 233 234 TEST_F(JSPipeNodeTest, InvalidIoctl) { 235 // 123 is not a valid ioctl request. 236 EXPECT_EQ(EINVAL, pipe_dev_->Ioctl(123)); 237 } 238 239 TEST_F(JSPipeNodeTest, JSPipeInput) { 240 std::string message("hello, how are you?\n"); 241 242 // First we send some data into the pipe. This is how messages 243 // from javascript are injected into the pipe nodes. 244 ASSERT_EQ(0, JSPipeInject(message.c_str())); 245 246 // Now we make buffer we'll read into. 247 // We fill the buffer and a backup buffer with arbitrary data 248 // and compare them after reading to make sure read doesn't 249 // clobber parts of the buffer it shouldn't. 250 int bytes_read; 251 char buffer[100]; 252 char backup_buffer[100]; 253 memset(buffer, 'a', sizeof(buffer)); 254 memset(backup_buffer, 'a', sizeof(backup_buffer)); 255 256 // We read a small chunk first to ensure it doesn't give us 257 // more than we ask for. 258 HandleAttr attrs; 259 ASSERT_EQ(0, pipe_dev_->Read(attrs, buffer, 5, &bytes_read)); 260 EXPECT_EQ(5, bytes_read); 261 EXPECT_EQ(0, memcmp(message.data(), buffer, 5)); 262 EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, sizeof(buffer)-5)); 263 264 // Now we ask for more data than is left in the pipe, to ensure 265 // it doesn't give us more than there is. 266 ASSERT_EQ(0, pipe_dev_->Read(attrs, buffer + 5, sizeof(buffer)-5, 267 &bytes_read)); 268 EXPECT_EQ(bytes_read, message.size() - 5); 269 EXPECT_EQ(0, memcmp(message.data(), buffer, message.size())); 270 EXPECT_EQ(0, memcmp(buffer + message.size(), 271 backup_buffer + message.size(), 272 100 - message.size())); 273 } 274 275 TEST_F(JSPipeNodeTest, JSPipeOutput) { 276 std::string message("hello"); 277 278 int bytes_written = 999; 279 HandleAttr attrs; 280 ASSERT_EQ(0, pipe_dev_->Write(attrs, message.c_str(), message.size(), 281 &bytes_written)); 282 ASSERT_EQ(message.size(), bytes_written); 283 284 FakeMessagingInterface* iface = 285 (FakeMessagingInterface*)ppapi_.GetMessagingInterface(); 286 287 // Verify that exactly one message sent. 288 ASSERT_EQ(1, iface->messages.size()); 289 PP_Var message_var = iface->messages[0]; 290 291 // Verify the content of the message. 292 VerifyPipeMessage(message_var, "jspipe1", "write", message.c_str(), 293 message.size()); 294 } 295 296 TEST_F(JSPipeNodeTest, JSPipeOutputWithNulls) { 297 char message[20]; 298 int message_len = sizeof(message); 299 300 // Construct a 20-byte message containing the string 'hello' but with 301 // null chars on either end. 302 memset(message, 0 , message_len); 303 memcpy(message+10, "hello", 5); 304 305 int bytes_written = 999; 306 HandleAttr attrs; 307 EXPECT_EQ(0, pipe_dev_->Write(attrs, message, message_len, &bytes_written)); 308 EXPECT_EQ(message_len, bytes_written); 309 310 // Verify that the correct messages was sent via PostMessage. 311 FakeMessagingInterface* iface = 312 (FakeMessagingInterface*)ppapi_.GetMessagingInterface(); 313 314 // Verify that exaclty one message sent. 315 ASSERT_EQ(1, iface->messages.size()); 316 PP_Var message_var = iface->messages[0]; 317 318 // Verify the content of the message. 319 VerifyPipeMessage(message_var, "jspipe1", "write", message, message_len); 320 } 321 322 #define CHUNK_SIZE 678 323 TEST_F(JSPipeNodeTest, JSPipeOutputBuffer) { 324 int ospace_orig = -1; 325 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETOSPACE, &ospace_orig)); 326 ASSERT_GT(ospace_orig, 0); 327 328 HandleAttr attrs; 329 attrs.flags = O_NONBLOCK; 330 char* message = (char*)malloc(CHUNK_SIZE); 331 332 // Keep writing data until we block. 333 int total_written = 0; 334 while (1) { 335 int bytes_written; 336 // Write some data 337 int rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written); 338 if (rtn != 0) { 339 ASSERT_EQ(EWOULDBLOCK, rtn); 340 int ospace = -1; 341 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETOSPACE, &ospace)); 342 ASSERT_EQ(0, ospace); 343 ASSERT_EQ(total_written, ospace_orig); 344 break; 345 } 346 total_written += bytes_written; 347 } 348 349 // At this point writes should always block 350 int bytes_written; 351 int rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written); 352 ASSERT_EQ(EWOULDBLOCK, rtn); 353 354 // Now inject and ACK message from JavaScript. 355 ASSERT_EQ(0, JSPipeInjectAck(10)); 356 357 // Now it should be possible to write 10 bytes to the pipe. 358 rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written); 359 ASSERT_EQ(0, rtn); 360 ASSERT_EQ(10, bytes_written); 361 362 free(message); 363 } 364 365 TEST_F(JSPipeNodeTest, JSPipeInputBuffer) { 366 char* message = (char*)malloc(CHUNK_SIZE); 367 memset(message, 1, CHUNK_SIZE); 368 369 int ispace_orig = -1; 370 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETISPACE, &ispace_orig)); 371 372 // Keep injecting data until the ioctl fails 373 int total_written = 0; 374 while (1) { 375 int rtn = JSPipeInject(message, CHUNK_SIZE); 376 if (rtn != 0) { 377 ASSERT_LT(total_written, ispace_orig); 378 ASSERT_GT(total_written, ispace_orig - CHUNK_SIZE - 1); 379 break; 380 } 381 total_written += CHUNK_SIZE; 382 } 383 384 int ispace = -1; 385 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETISPACE, &ispace)); 386 ASSERT_EQ(0, ispace); 387 388 // Check that no messages have thus far been sent to JavaScript 389 FakeMessagingInterface* iface = 390 (FakeMessagingInterface*)ppapi_.GetMessagingInterface(); 391 ASSERT_EQ(0, iface->messages.size()); 392 393 // Read some data from the pipe, which should trigger an ack message 394 int bytes_read = -1; 395 HandleAttr attrs; 396 ASSERT_EQ(0, pipe_dev_->Read(attrs, message, 5, &bytes_read)); 397 ASSERT_EQ(5, bytes_read); 398 399 // Verify that an ack was sent to JavaScript 400 ASSERT_EQ(1, iface->messages.size()); 401 PP_Var message_var = iface->messages[0]; 402 VerifyPipeMessage(message_var, "jspipe1", "ack", NULL, 0, 5); 403 404 free(message); 405 } 406 407 // Returns: 408 // 0 -> Not readable 409 // 1 -> Readable 410 // -1 -> Error occured 411 int IsReadable(int fd) { 412 struct timeval timeout = {0, 0}; 413 fd_set readfds; 414 fd_set errorfds; 415 FD_ZERO(&readfds); 416 FD_ZERO(&errorfds); 417 FD_SET(fd, &readfds); 418 FD_SET(fd, &errorfds); 419 int rtn = ki_select(fd + 1, &readfds, NULL, &errorfds, &timeout); 420 if (rtn == 0) 421 return 0; // not readable 422 if (rtn != 1) 423 return -1; // error 424 if (FD_ISSET(fd, &errorfds)) 425 return -2; // error 426 if (!FD_ISSET(fd, &readfds)) 427 return -3; // error 428 return 1; // readable 429 } 430 431 TEST_F(JSPipeTest, JSPipeSelect) { 432 struct timeval timeout; 433 fd_set readfds; 434 fd_set writefds; 435 fd_set errorfds; 436 437 int pipe_fd = ki_open("/dev/jspipe1", O_RDONLY, 0); 438 ASSERT_GT(pipe_fd, 0) << "jspipe1 open failed: " << errno; 439 440 FD_ZERO(&readfds); 441 FD_ZERO(&errorfds); 442 FD_SET(pipe_fd, &readfds); 443 FD_SET(pipe_fd, &errorfds); 444 // 10 millisecond timeout 445 timeout.tv_sec = 0; 446 timeout.tv_usec = 10 * 1000; 447 // Should timeout when no input is available. 448 int rtn = ki_select(pipe_fd + 1, &readfds, NULL, &errorfds, &timeout); 449 ASSERT_EQ(0, rtn) << "select failed: " << rtn << " err=" << strerror(errno); 450 ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds)); 451 ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds)); 452 453 FD_ZERO(&readfds); 454 FD_ZERO(&writefds); 455 FD_ZERO(&errorfds); 456 FD_SET(pipe_fd, &readfds); 457 FD_SET(pipe_fd, &writefds); 458 FD_SET(pipe_fd, &errorfds); 459 // Pipe should be writable on startup. 460 rtn = ki_select(pipe_fd + 1, &readfds, &writefds, &errorfds, NULL); 461 ASSERT_EQ(1, rtn); 462 ASSERT_TRUE(FD_ISSET(pipe_fd, &writefds)); 463 ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds)); 464 ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds)); 465 466 // Send 4 bytes to the pipe via ioctl 467 PP_Var message = CreateWriteMessage(&ppapi_, "jspipe1", "test"); 468 ASSERT_EQ(0, ki_ioctl_wrapper(pipe_fd, NACL_IOC_HANDLEMESSAGE, &message)); 469 ppapi_.GetVarInterface()->Release(message); 470 471 // Pipe should now be readable 472 ASSERT_EQ(1, IsReadable(pipe_fd)); 473 474 ki_close(pipe_fd); 475 } 476 477 } 478