LCOV - code coverage report
Current view: top level - source3/torture - test_messaging_fd_passing.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 150 209 71.8 %
Date: 2024-04-21 15:09:00 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Test for fd passing with messaging
       4             : 
       5             :    Copyright (C) Michael Adam 2014
       6             : 
       7             :    This program is free software; you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation; either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "includes.h"
      22             : #include "torture/proto.h"
      23             : #include "lib/util/tevent_unix.h"
      24             : #include "messages.h"
      25             : 
      26             : /**
      27             :  * test fdpass1:
      28             :  *
      29             :  * Try to pass an fd to the sending process - fails.
      30             :  */
      31           1 : bool run_messaging_fdpass1(int dummy)
      32             : {
      33           1 :         struct tevent_context *ev = NULL;
      34           1 :         struct messaging_context *msg_ctx = NULL;
      35           1 :         bool retval = false;
      36           1 :         int pipe_fds[2];
      37           1 :         int pass_fds[1] = { 0 };
      38           1 :         int ret;
      39           1 :         NTSTATUS status;
      40           1 :         struct server_id dst;
      41           1 :         TALLOC_CTX *frame = talloc_stackframe();
      42             : 
      43           1 :         ev = samba_tevent_context_init(frame);
      44           1 :         if (ev == NULL) {
      45           0 :                 fprintf(stderr, "tevent_context_init failed\n");
      46           0 :                 goto fail;
      47             :         }
      48           1 :         msg_ctx = messaging_init(ev, ev);
      49           1 :         if (msg_ctx == NULL) {
      50           0 :                 fprintf(stderr, "messaging_init failed\n");
      51           0 :                 goto fail;
      52             :         }
      53             : 
      54           1 :         dst = messaging_server_id(msg_ctx);
      55             : 
      56           1 :         ret = pipe(pipe_fds);
      57           1 :         if (ret != 0) {
      58           0 :                 perror("pipe failed");
      59           0 :                 goto fail;
      60             :         }
      61             : 
      62           1 :         pass_fds[0] = pipe_fds[0];
      63             : 
      64           1 :         status = messaging_send_iov(msg_ctx, dst, MSG_PING, NULL, 0,
      65             :                                     pass_fds, 1);
      66           1 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
      67           0 :                 fprintf(stderr,
      68             :                         "messaging_send_iov gave: %s\n", nt_errstr(status));
      69           0 :                 goto fail;
      70             :         }
      71             : 
      72           0 :         retval = true;
      73             : 
      74           1 : fail:
      75           1 :         TALLOC_FREE(frame);
      76           1 :         return retval;
      77             : }
      78             : 
      79             : /**
      80             :  * test fdpass2:
      81             :  *
      82             :  * - parent: create a child
      83             :  * - parent: create a two pipes in the parent: up and down
      84             :  * - parent: pass the up pipe's reading end and the down pipe's writing
      85             :  *   end to the child and close them
      86             :  * - parent: write a number into the up pipe's writing end
      87             :  * - child: read number from the passed reading fd (up)
      88             :  * - child: write the read number to the passed writing fd (down)
      89             :  * - parent: read number from the down pipe's reading end and compare with
      90             :  *   original number
      91             :  */
      92             : 
      93             : #define MSG_TORTURE_FDPASS2 0xF002
      94             : 
      95           3 : static bool fdpass2_filter(struct messaging_rec *rec, void *private_data)
      96             : {
      97           3 :         if (rec->msg_type != MSG_TORTURE_FDPASS2) {
      98           0 :                 return false;
      99             :         }
     100             : 
     101           3 :         if (rec->num_fds != 2) {
     102           0 :                 return false;
     103             :         }
     104             : 
     105           0 :         return true;
     106             : }
     107             : 
     108           3 : static bool fdpass2_child(int ready_fd)
     109             : {
     110           3 :         struct tevent_context *ev = NULL;
     111           3 :         struct messaging_context *msg_ctx = NULL;
     112           3 :         TALLOC_CTX *frame = talloc_stackframe();
     113           3 :         bool retval = false;
     114           3 :         uint8_t c = 1;
     115           3 :         struct tevent_req *subreq;
     116           3 :         int ret;
     117           3 :         ssize_t bytes;
     118           3 :         int up_fd, down_fd;
     119           3 :         struct messaging_rec *rec;
     120           3 :         bool ok;
     121             : 
     122           3 :         ev = samba_tevent_context_init(frame);
     123           3 :         if (ev == NULL) {
     124           0 :                 fprintf(stderr, "child: tevent_context_init failed\n");
     125           0 :                 goto done;
     126             :         }
     127             : 
     128           3 :         msg_ctx = messaging_init(ev, ev);
     129           3 :         if (msg_ctx == NULL) {
     130           0 :                 fprintf(stderr, "child: messaging_init failed\n");
     131           0 :                 goto done;
     132             :         }
     133             : 
     134             :         /* Tell the parent we are ready to receive messages. */
     135           3 :         bytes = write(ready_fd, &c, 1);
     136           3 :         if (bytes != 1) {
     137           0 :                 perror("child: failed to write to ready_fd");
     138           0 :                 goto done;
     139             :         }
     140             : 
     141           3 :         subreq = messaging_filtered_read_send(frame, /* TALLOC_CTX */
     142             :                                               ev, msg_ctx,
     143             :                                               fdpass2_filter, NULL);
     144           3 :         if (subreq == NULL) {
     145           0 :                 fprintf(stderr, "child: messaging_filtered_read_send failed\n");
     146           0 :                 goto done;
     147             :         }
     148             : 
     149           3 :         ok = tevent_req_poll(subreq, ev);
     150           3 :         if (!ok) {
     151           0 :                 fprintf(stderr, "child: tevent_req_poll failed\n");
     152           0 :                 goto done;
     153             :         }
     154             : 
     155           3 :         ret = messaging_filtered_read_recv(subreq, frame, &rec);
     156           3 :         TALLOC_FREE(subreq);
     157           3 :         if (ret != 0) {
     158           0 :                 fprintf(stderr, "child: messaging_filtered_read_recv failed\n");
     159           0 :                 goto done;
     160             :         }
     161             : 
     162           3 :         SMB_ASSERT(rec->num_fds == 2);
     163             : 
     164             :         /* Tell the parent we are done. */
     165           3 :         bytes = write(ready_fd, &c, 1);
     166           3 :         if (bytes != 1) {
     167           0 :                 perror("child: failed to write to ready_fd");
     168           0 :                 goto done;
     169             :         }
     170             : 
     171           3 :         up_fd = rec->fds[0];
     172           3 :         down_fd = rec->fds[1];
     173             : 
     174           3 :         bytes = read(up_fd, &c, 1);
     175           3 :         if (bytes != 1) {
     176           0 :                 perror("child: read from up_fd failed");
     177           0 :                 goto done;
     178             :         }
     179             : 
     180           3 :         bytes = write(down_fd, &c, 1);
     181           3 :         if (bytes != 1) {
     182           0 :                 perror("child: write to down_fd failed");
     183             :         }
     184             : 
     185           3 :         printf("child: done\n");
     186             : 
     187           3 :         retval = true;
     188             : 
     189           3 : done:
     190           3 :         TALLOC_FREE(frame);
     191           3 :         return retval;
     192             : }
     193             : 
     194             : struct child_done_state {
     195             :         int fd;
     196             :         bool done;
     197             : };
     198             : 
     199           3 : static void child_done_cb(struct tevent_context *ev,
     200             :                           struct tevent_fd *fde,
     201             :                           uint16_t flags,
     202             :                           void *private_data)
     203             : {
     204           3 :         struct child_done_state *state =
     205             :                         (struct child_done_state *)private_data;
     206           3 :         char c = 0;
     207           3 :         ssize_t bytes;
     208             : 
     209           3 :         bytes = read(state->fd, &c, 1);
     210           3 :         if (bytes != 1) {
     211           0 :                 perror("parent: read from ready_fd failed");
     212             :         }
     213             : 
     214           3 :         state->done = true;
     215           3 : }
     216             : 
     217           3 : static bool fdpass2_parent(pid_t child_pid, int ready_fd, size_t payload_size)
     218             : {
     219           3 :         struct tevent_context *ev = NULL;
     220           3 :         struct messaging_context *msg_ctx = NULL;
     221           3 :         bool retval = false;
     222           3 :         int up_pipe[2];
     223           3 :         int down_pipe[2];
     224           3 :         int pass_fds[2] = { 0 };
     225           3 :         int ret;
     226           3 :         NTSTATUS status;
     227           3 :         struct server_id dst;
     228           3 :         TALLOC_CTX *frame = talloc_stackframe();
     229           3 :         uint8_t c1 = 1, c2, c;
     230           3 :         ssize_t bytes;
     231           3 :         struct iovec iov;
     232           3 :         DATA_BLOB blob;
     233           3 :         struct tevent_fd *child_done_fde;
     234           3 :         struct child_done_state child_state;
     235             : 
     236           3 :         ev = samba_tevent_context_init(frame);
     237           3 :         if (ev == NULL) {
     238           0 :                 fprintf(stderr, "parent: tevent_context_init failed\n");
     239           0 :                 goto done;
     240             :         }
     241             : 
     242           3 :         msg_ctx = messaging_init(ev, ev);
     243           3 :         if (msg_ctx == NULL) {
     244           0 :                 fprintf(stderr, "parent: messaging_init failed\n");
     245           0 :                 goto done;
     246             :         }
     247             : 
     248             :         /* wait util the child is ready to receive messages */
     249           3 :         bytes = read(ready_fd, &c, 1);
     250           3 :         if (bytes != 1) {
     251           0 :                 perror("parent: read from ready_fd failed");
     252           0 :                 goto done;
     253             :         }
     254             : 
     255           3 :         ret = pipe(up_pipe);
     256           3 :         if (ret != 0) {
     257           0 :                 perror("parent: pipe failed for up_pipe");
     258           0 :                 goto done;
     259             :         }
     260             : 
     261           3 :         ret = pipe(down_pipe);
     262           3 :         if (ret != 0) {
     263           0 :                 perror("parent: pipe failed for down_pipe");
     264           0 :                 goto done;
     265             :         }
     266             : 
     267           3 :         child_state.fd = ready_fd;
     268           3 :         child_state.done = false;
     269             : 
     270           3 :         child_done_fde = tevent_add_fd(ev, ev, ready_fd, TEVENT_FD_READ,
     271             :                                        child_done_cb, &child_state);
     272           3 :         if (child_done_fde == NULL) {
     273           0 :                 fprintf(stderr,
     274             :                         "parent: failed tevent_add_fd for child done\n");
     275           0 :                 goto done;
     276             :         }
     277             : 
     278           3 :         pass_fds[0] = up_pipe[0];
     279           3 :         pass_fds[1] = down_pipe[1];
     280             : 
     281           3 :         dst = messaging_server_id(msg_ctx);
     282           3 :         dst.pid = child_pid;
     283             : 
     284             :         /*
     285             :          * Send a certain payload with the fds, to test to test
     286             :          * that fd-passing works when we have fragmentation and
     287             :          * re-assembly of the datagrams.
     288             :          *
     289             :          * Fragmentation/queuing is triggered by a certain payload
     290             :          * size. Payloads below that size use the fast path.
     291             :          */
     292           3 :         blob = data_blob_talloc_zero(frame, payload_size);
     293           3 :         iov.iov_base = blob.data;
     294           3 :         iov.iov_len  = blob.length;
     295             : 
     296           3 :         status = messaging_send_iov(msg_ctx, dst, MSG_TORTURE_FDPASS2, &iov, 1,
     297             :                                     pass_fds, 2);
     298           3 :         if (!NT_STATUS_IS_OK(status)) {
     299           0 :                 fprintf(stderr, "parent: messaging_send_iov failed: %s\n",
     300             :                         nt_errstr(status));
     301           0 :                 goto done;
     302             :         }
     303             : 
     304           3 :         printf("parent: waiting for child to confirm\n");
     305             : 
     306        2973 :         while (!child_state.done) {
     307        2970 :                 ret = tevent_loop_once(ev);
     308        2970 :                 if (ret != 0) {
     309           0 :                         fprintf(stderr, "parent: tevent_loop_once failed\n");
     310           0 :                         goto done;
     311             :                 }
     312             :         }
     313             : 
     314           3 :         printf("parent: child confirmed\n");
     315             : 
     316           3 :         close(up_pipe[0]);
     317           3 :         close(down_pipe[1]);
     318             : 
     319           3 :         bytes = write(up_pipe[1], &c1, 1);
     320           3 :         if (bytes != 1) {
     321           0 :                 perror("parent: write to up pipe failed");
     322           0 :                 goto done;
     323             :         }
     324             : 
     325           3 :         bytes = read(down_pipe[0], &c2, 1);
     326           3 :         if (bytes != 1) {
     327           0 :                 perror("parent: read from down pipe failed");
     328           0 :                 goto done;
     329             :         }
     330             : 
     331           3 :         if (c1 != c2) {
     332           0 :                 fprintf(stderr, "parent: c1[%d] != c2[%d]\n", c1, c2);
     333           0 :                 goto done;
     334             :         }
     335             : 
     336           3 :         ret = waitpid(child_pid, NULL, 0);
     337           3 :         if (ret == -1) {
     338           0 :                 perror("parent: waitpid failed");
     339           0 :                 goto done;
     340             :         }
     341             : 
     342           0 :         retval = true;
     343             : 
     344           3 : done:
     345           3 :         TALLOC_FREE(frame);
     346           3 :         return retval;
     347             : }
     348             : 
     349           3 : static bool run_messaging_fdpass2_int(int dummy, size_t payload_size)
     350             : {
     351           3 :         bool retval = false;
     352           3 :         pid_t child_pid;
     353           3 :         int ready_pipe[2];
     354           3 :         int ret;
     355             : 
     356           3 :         ret = pipe(ready_pipe);
     357           3 :         if (ret != 0) {
     358           0 :                 perror("parent: pipe failed for ready_pipe");
     359           0 :                 return retval;
     360             :         }
     361             : 
     362           3 :         child_pid = fork();
     363           6 :         if (child_pid == -1) {
     364           0 :                 perror("fork failed");
     365           6 :         } else if (child_pid == 0) {
     366           3 :                 close(ready_pipe[0]);
     367           3 :                 retval = fdpass2_child(ready_pipe[1]);
     368             :         } else {
     369           3 :                 close(ready_pipe[1]);
     370           3 :                 retval = fdpass2_parent(child_pid, ready_pipe[0], payload_size);
     371             :         }
     372             : 
     373           0 :         return retval;
     374             : }
     375             : 
     376           1 : bool run_messaging_fdpass2(int dummy)
     377             : {
     378           1 :         return run_messaging_fdpass2_int(dummy, 1000*1000);
     379             : }
     380             : 
     381             : /**
     382             :  * Variant of the FDPASS2 test that tests the non-queuing fast path
     383             :  * with a small payload.
     384             :  */
     385           1 : bool run_messaging_fdpass2a(int dummy)
     386             : {
     387           1 :         return run_messaging_fdpass2_int(dummy, 1);
     388             : }
     389             : 
     390             : /**
     391             :  * Variant of the FDPASS2 test that tests the non-queuing fast path
     392             :  * without a payload.
     393             :  */
     394           1 : bool run_messaging_fdpass2b(int dummy)
     395             : {
     396           1 :         return run_messaging_fdpass2_int(dummy, 0);
     397             : }

Generated by: LCOV version 1.14