jspipe_test.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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 24using namespace nacl_io; 25 26namespace { 27 28// Helper function for calling ki_ioctl without having 29// to construct a va_list. 30int 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 39std::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 46PP_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 ] 52PP_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. 80PP_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. 87PP_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 103class 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 119class JSPipeNodeTest : public ::testing::Test { 120 public: 121 JSPipeNodeTest() : fs_(&ppapi_) {} 122 123 void SetUp() { 124 name_ = "jspipe1"; 125 ASSERT_EQ(0, fs_.Access(Path("/jspipe1"), R_OK | W_OK)); 126 ASSERT_EQ(EACCES, fs_.Access(Path("/jspipe1"), X_OK)); 127 ASSERT_EQ(0, fs_.Open(Path("/jspipe1"), O_RDWR, &pipe_dev_)); 128 ASSERT_NE(NULL_NODE, pipe_dev_.get()); 129 } 130 131 /** 132 * Create a PP_Var message in the same way that we expect 133 * JavaScript code to, and send it to the pipe using ioctl() 134 */ 135 int JSPipeInject(const char* string, int length=-1) { 136 PP_Var message = CreateWriteMessage(&ppapi_, name_, string, length); 137 138 // Send the message via ioctl 139 int rtn = pipe_dev_->Ioctl(NACL_IOC_HANDLEMESSAGE, &message); 140 141 // Release message 142 ppapi_.GetVarInterface()->Release(message); 143 return rtn; 144 } 145 146 int JSPipeInjectAck(int32_t count) { 147 PP_Var message = CreateAckMessage(&ppapi_, name_, count); 148 149 // Send the message via ioctl 150 int rtn = pipe_dev_->Ioctl(NACL_IOC_HANDLEMESSAGE, &message); 151 152 // Release message 153 ppapi_.GetVarInterface()->Release(message); 154 return rtn; 155 } 156 157 // Verify the contents of the jspipe mesage, which should be 158 // { 159 // "pipe": '<pipe_name>', 160 // "operation": '<command_name>', 161 // "payload": payload 162 // } 163 void VerifyPipeMessage(PP_Var message, 164 const char* pipe_name, 165 const char* operation, 166 const char* payload, 167 int payload_length, 168 int32_t int_payload=0) { 169 VarArrayInterface* array_iface = ppapi_.GetVarArrayInterface(); 170 VarDictionaryInterface* dict_iface = ppapi_.GetVarDictionaryInterface(); 171 VarInterface* var_iface = ppapi_.GetVarInterface(); 172 VarArrayBufferInterface* buffer_iface = ppapi_.GetVarArrayBufferInterface(); 173 174 // Verify we have a dictionary with 3 keys 175 ASSERT_EQ(PP_VARTYPE_DICTIONARY, message.type); 176 PP_Var keys = dict_iface->GetKeys(message); 177 ASSERT_EQ(PP_VARTYPE_ARRAY, keys.type); 178 ASSERT_EQ(3, array_iface->GetLength(keys)); 179 var_iface->Release(keys); 180 181 // Verify the keys 182 PP_Var key1 = VarFromCStr(var_iface, "pipe"); 183 PP_Var key2 = VarFromCStr(var_iface, "operation"); 184 PP_Var key3 = VarFromCStr(var_iface, "payload"); 185 186 // Verify pipe name and operation values 187 PP_Var value1 = dict_iface->Get(message, key1); 188 ASSERT_STREQ(pipe_name, VarToString(var_iface, value1).c_str()); 189 var_iface->Release(value1); 190 var_iface->Release(key1); 191 192 PP_Var value2 = dict_iface->Get(message, key2); 193 ASSERT_STREQ(operation, VarToString(var_iface, value2).c_str()); 194 var_iface->Release(value2); 195 var_iface->Release(key2); 196 197 // Verify the payload 198 PP_Var payload_var = dict_iface->Get(message, key3); 199 if (payload != NULL) { 200 ASSERT_EQ(PP_VARTYPE_ARRAY_BUFFER, payload_var.type); 201 ASSERT_EQ(0, memcmp(payload, buffer_iface->Map(payload_var), 202 payload_length)); 203 } else { 204 ASSERT_EQ(PP_VARTYPE_INT32, payload_var.type); 205 ASSERT_EQ(int_payload, payload_var.value.as_int); 206 } 207 var_iface->Release(key3); 208 var_iface->Release(payload_var); 209 } 210 211 protected: 212 FakePepperInterface ppapi_; 213 DevFsForTesting fs_; 214 ScopedNode pipe_dev_; 215 const char* name_; 216}; 217 218TEST(JSPipeTestBasic, MissingPepper) { 219 // Create a devfs filesystem without giving it any Pepper implemenation. 220 TypedFsFactory<DevFs> factory; 221 ScopedFilesystem fs; 222 FsInitArgs args(1); 223 factory.CreateFilesystem(args, &fs); 224 ScopedNode pipe_dev; 225 ASSERT_EQ(0, fs->Open(Path("/jspipe1"), O_RDWR, &pipe_dev)); 226 227 // Writing to a pipe should return EIO because Pepper is missing. 228 HandleAttr attrs; 229 int written = -1; 230 ASSERT_EQ(EIO, pipe_dev->Write(attrs, "test", 4, &written)); 231} 232 233TEST_F(JSPipeNodeTest, InvalidIoctl) { 234 // 123 is not a valid ioctl request. 235 EXPECT_EQ(EINVAL, pipe_dev_->Ioctl(123)); 236} 237 238TEST_F(JSPipeNodeTest, JSPipeInput) { 239 std::string message("hello, how are you?\n"); 240 241 // First we send some data into the pipe. This is how messages 242 // from javascript are injected into the pipe nodes. 243 ASSERT_EQ(0, JSPipeInject(message.c_str())); 244 245 // Now we make buffer we'll read into. 246 // We fill the buffer and a backup buffer with arbitrary data 247 // and compare them after reading to make sure read doesn't 248 // clobber parts of the buffer it shouldn't. 249 int bytes_read; 250 char buffer[100]; 251 char backup_buffer[100]; 252 memset(buffer, 'a', sizeof(buffer)); 253 memset(backup_buffer, 'a', sizeof(backup_buffer)); 254 255 // We read a small chunk first to ensure it doesn't give us 256 // more than we ask for. 257 HandleAttr attrs; 258 ASSERT_EQ(0, pipe_dev_->Read(attrs, buffer, 5, &bytes_read)); 259 EXPECT_EQ(5, bytes_read); 260 EXPECT_EQ(0, memcmp(message.data(), buffer, 5)); 261 EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, sizeof(buffer)-5)); 262 263 // Now we ask for more data than is left in the pipe, to ensure 264 // it doesn't give us more than there is. 265 ASSERT_EQ(0, pipe_dev_->Read(attrs, buffer + 5, sizeof(buffer)-5, 266 &bytes_read)); 267 EXPECT_EQ(bytes_read, message.size() - 5); 268 EXPECT_EQ(0, memcmp(message.data(), buffer, message.size())); 269 EXPECT_EQ(0, memcmp(buffer + message.size(), 270 backup_buffer + message.size(), 271 100 - message.size())); 272} 273 274TEST_F(JSPipeNodeTest, JSPipeOutput) { 275 std::string message("hello"); 276 277 int bytes_written = 999; 278 HandleAttr attrs; 279 ASSERT_EQ(0, pipe_dev_->Write(attrs, message.c_str(), message.size(), 280 &bytes_written)); 281 ASSERT_EQ(message.size(), bytes_written); 282 283 FakeMessagingInterface* iface = 284 (FakeMessagingInterface*)ppapi_.GetMessagingInterface(); 285 286 // Verify that exactly one message sent. 287 ASSERT_EQ(1, iface->messages.size()); 288 PP_Var message_var = iface->messages[0]; 289 290 // Verify the content of the message. 291 VerifyPipeMessage(message_var, "jspipe1", "write", message.c_str(), 292 message.size()); 293} 294 295TEST_F(JSPipeNodeTest, JSPipeOutputWithNulls) { 296 char message[20]; 297 int message_len = sizeof(message); 298 299 // Construct a 20-byte message containing the string 'hello' but with 300 // null chars on either end. 301 memset(message, 0 , message_len); 302 memcpy(message+10, "hello", 5); 303 304 int bytes_written = 999; 305 HandleAttr attrs; 306 EXPECT_EQ(0, pipe_dev_->Write(attrs, message, message_len, &bytes_written)); 307 EXPECT_EQ(message_len, bytes_written); 308 309 // Verify that the correct messages was sent via PostMessage. 310 FakeMessagingInterface* iface = 311 (FakeMessagingInterface*)ppapi_.GetMessagingInterface(); 312 313 // Verify that exaclty one message sent. 314 ASSERT_EQ(1, iface->messages.size()); 315 PP_Var message_var = iface->messages[0]; 316 317 // Verify the content of the message. 318 VerifyPipeMessage(message_var, "jspipe1", "write", message, message_len); 319} 320 321#define CHUNK_SIZE 678 322TEST_F(JSPipeNodeTest, JSPipeOutputBuffer) { 323 int ospace_orig = -1; 324 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETOSPACE, &ospace_orig)); 325 ASSERT_GT(ospace_orig, 0); 326 327 HandleAttr attrs; 328 attrs.flags = O_NONBLOCK; 329 char* message = (char*)malloc(CHUNK_SIZE); 330 331 // Keep writing data until we block. 332 int total_written = 0; 333 while (1) { 334 int bytes_written; 335 // Write some data 336 int rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written); 337 if (rtn != 0) { 338 ASSERT_EQ(EWOULDBLOCK, rtn); 339 int ospace = -1; 340 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETOSPACE, &ospace)); 341 ASSERT_EQ(0, ospace); 342 ASSERT_EQ(total_written, ospace_orig); 343 break; 344 } 345 total_written += bytes_written; 346 } 347 348 // At this point writes should always block 349 int bytes_written; 350 int rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written); 351 ASSERT_EQ(EWOULDBLOCK, rtn); 352 353 // Now inject and ACK message from JavaScript. 354 ASSERT_EQ(0, JSPipeInjectAck(10)); 355 356 // Now it should be possible to write 10 bytes to the pipe. 357 rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written); 358 ASSERT_EQ(0, rtn); 359 ASSERT_EQ(10, bytes_written); 360 361 free(message); 362} 363 364TEST_F(JSPipeNodeTest, JSPipeInputBuffer) { 365 char* message = (char*)malloc(CHUNK_SIZE); 366 memset(message, 1, CHUNK_SIZE); 367 368 int ispace_orig = -1; 369 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETISPACE, &ispace_orig)); 370 371 // Keep injecting data until the ioctl fails 372 int total_written = 0; 373 while (1) { 374 int rtn = JSPipeInject(message, CHUNK_SIZE); 375 if (rtn != 0) { 376 ASSERT_LT(total_written, ispace_orig); 377 ASSERT_GT(total_written, ispace_orig - CHUNK_SIZE - 1); 378 break; 379 } 380 total_written += CHUNK_SIZE; 381 } 382 383 int ispace = -1; 384 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETISPACE, &ispace)); 385 ASSERT_EQ(0, ispace); 386 387 // Check that no messages have thus far been sent to JavaScript 388 FakeMessagingInterface* iface = 389 (FakeMessagingInterface*)ppapi_.GetMessagingInterface(); 390 ASSERT_EQ(0, iface->messages.size()); 391 392 // Read some data from the pipe, which should trigger an ack message 393 int bytes_read = -1; 394 HandleAttr attrs; 395 ASSERT_EQ(0, pipe_dev_->Read(attrs, message, 5, &bytes_read)); 396 ASSERT_EQ(5, bytes_read); 397 398 // Verify that an ack was sent to JavaScript 399 ASSERT_EQ(1, iface->messages.size()); 400 PP_Var message_var = iface->messages[0]; 401 VerifyPipeMessage(message_var, "jspipe1", "ack", NULL, 0, 5); 402 403 free(message); 404} 405 406// Returns: 407// 0 -> Not readable 408// 1 -> Readable 409// -1 -> Error occured 410int IsReadable(int fd) { 411 struct timeval timeout = {0, 0}; 412 fd_set readfds; 413 fd_set errorfds; 414 FD_ZERO(&readfds); 415 FD_ZERO(&errorfds); 416 FD_SET(fd, &readfds); 417 FD_SET(fd, &errorfds); 418 int rtn = ki_select(fd + 1, &readfds, NULL, &errorfds, &timeout); 419 if (rtn == 0) 420 return 0; // not readable 421 if (rtn != 1) 422 return -1; // error 423 if (FD_ISSET(fd, &errorfds)) 424 return -2; // error 425 if (!FD_ISSET(fd, &readfds)) 426 return -3; // error 427 return 1; // readable 428} 429 430TEST_F(JSPipeTest, JSPipeSelect) { 431 struct timeval timeout; 432 fd_set readfds; 433 fd_set writefds; 434 fd_set errorfds; 435 436 int pipe_fd = ki_open("/dev/jspipe1", O_RDONLY); 437 ASSERT_GT(pipe_fd, 0) << "jspipe1 open failed: " << errno; 438 439 FD_ZERO(&readfds); 440 FD_ZERO(&errorfds); 441 FD_SET(pipe_fd, &readfds); 442 FD_SET(pipe_fd, &errorfds); 443 // 10 millisecond timeout 444 timeout.tv_sec = 0; 445 timeout.tv_usec = 10 * 1000; 446 // Should timeout when no input is available. 447 int rtn = ki_select(pipe_fd + 1, &readfds, NULL, &errorfds, &timeout); 448 ASSERT_EQ(0, rtn) << "select failed: " << rtn << " err=" << strerror(errno); 449 ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds)); 450 ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds)); 451 452 FD_ZERO(&readfds); 453 FD_ZERO(&writefds); 454 FD_ZERO(&errorfds); 455 FD_SET(pipe_fd, &readfds); 456 FD_SET(pipe_fd, &writefds); 457 FD_SET(pipe_fd, &errorfds); 458 // Pipe should be writable on startup. 459 rtn = ki_select(pipe_fd + 1, &readfds, &writefds, &errorfds, NULL); 460 ASSERT_EQ(1, rtn); 461 ASSERT_TRUE(FD_ISSET(pipe_fd, &writefds)); 462 ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds)); 463 ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds)); 464 465 // Send 4 bytes to the pipe via ioctl 466 PP_Var message = CreateWriteMessage(&ppapi_, "jspipe1", "test"); 467 ASSERT_EQ(0, ki_ioctl_wrapper(pipe_fd, NACL_IOC_HANDLEMESSAGE, &message)); 468 ppapi_.GetVarInterface()->Release(message); 469 470 // Pipe should now be readable 471 ASSERT_EQ(1, IsReadable(pipe_fd)); 472 473 ki_close(pipe_fd); 474} 475 476} 477