Home | History | Annotate | Download | only in nacl_io_test
      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