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