~ubuntu-branches/ubuntu/natty/eglibc/natty-security

« back to all changes in this revision

Viewing changes to hurd/intr-msg.c

  • Committer: Bazaar Package Importer
  • Author(s): Aurelien Jarno
  • Date: 2009-05-05 09:54:14 UTC
  • Revision ID: james.westby@ubuntu.com-20090505095414-c45qsg9ixjheohru
ImportĀ upstreamĀ versionĀ 2.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Replacement for mach_msg used in interruptible Hurd RPCs.
 
2
   Copyright (C) 1995,96,97,98,99,2000,2001,2002,2005
 
3
        Free Software Foundation, Inc.
 
4
   This file is part of the GNU C Library.
 
5
 
 
6
   The GNU C Library is free software; you can redistribute it and/or
 
7
   modify it under the terms of the GNU Lesser General Public
 
8
   License as published by the Free Software Foundation; either
 
9
   version 2.1 of the License, or (at your option) any later version.
 
10
 
 
11
   The GNU C Library is distributed in the hope that it will be useful,
 
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
   Lesser General Public License for more details.
 
15
 
 
16
   You should have received a copy of the GNU Lesser General Public
 
17
   License along with the GNU C Library; if not, write to the Free
 
18
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 
19
   02111-1307 USA.  */
 
20
 
 
21
#include <mach.h>
 
22
#include <mach/mig_errors.h>
 
23
#include <mach/mig_support.h>
 
24
#include <hurd/signal.h>
 
25
#include <assert.h>
 
26
 
 
27
#include "intr-msg.h"
 
28
 
 
29
#ifdef NDR_CHAR_ASCII           /* OSF Mach flavors have different names.  */
 
30
# define mig_reply_header_t     mig_reply_error_t
 
31
#endif
 
32
 
 
33
error_t
 
34
_hurd_intr_rpc_mach_msg (mach_msg_header_t *msg,
 
35
                         mach_msg_option_t option,
 
36
                         mach_msg_size_t send_size,
 
37
                         mach_msg_size_t rcv_size,
 
38
                         mach_port_t rcv_name,
 
39
                         mach_msg_timeout_t timeout,
 
40
                         mach_port_t notify)
 
41
{
 
42
  error_t err;
 
43
  struct hurd_sigstate *ss;
 
44
  const mach_msg_option_t user_option = option;
 
45
  const mach_msg_timeout_t user_timeout = timeout;
 
46
 
 
47
  struct clobber
 
48
  {
 
49
#ifdef NDR_CHAR_ASCII
 
50
    NDR_record_t ndr;
 
51
#else
 
52
    mach_msg_type_t type;
 
53
#endif
 
54
    error_t err;
 
55
  };
 
56
  union msg
 
57
  {
 
58
    mach_msg_header_t header;
 
59
    mig_reply_header_t reply;
 
60
    struct
 
61
    {
 
62
      mach_msg_header_t header;
 
63
#ifdef NDR_CHAR_ASCII
 
64
      NDR_record_t ndr;
 
65
#else
 
66
      int type;
 
67
#endif
 
68
      int code;
 
69
    } check;
 
70
    struct
 
71
    {
 
72
      mach_msg_header_t header;
 
73
      struct clobber data;
 
74
    } request;
 
75
  };
 
76
  union msg *const m = (void *) msg;
 
77
  mach_msg_bits_t msgh_bits;
 
78
  mach_port_t remote_port;
 
79
  mach_msg_id_t msgid;
 
80
  struct clobber save_data;
 
81
 
 
82
  if ((option & (MACH_SEND_MSG|MACH_RCV_MSG)) != (MACH_SEND_MSG|MACH_RCV_MSG)
 
83
      || _hurd_msgport_thread == MACH_PORT_NULL)
 
84
    {
 
85
      /* Either this is not an RPC (i.e., only a send or only a receive),
 
86
         so it can't be interruptible; or, the signal thread is not set up
 
87
         yet, so we cannot do the normal signal magic.  Do a normal,
 
88
         uninterruptible mach_msg call instead.  */
 
89
      return __mach_msg (&m->header, option, send_size, rcv_size, rcv_name,
 
90
                         timeout, notify);
 
91
    }
 
92
 
 
93
  ss = _hurd_self_sigstate ();
 
94
 
 
95
  /* Save state that gets clobbered by an EINTR reply message.
 
96
     We will need to restore it if we want to retry the RPC.  */
 
97
  msgh_bits = m->header.msgh_bits;
 
98
  remote_port = m->header.msgh_remote_port;
 
99
  msgid = m->header.msgh_id;
 
100
  assert (rcv_size >= sizeof m->request);
 
101
  save_data = m->request.data;
 
102
 
 
103
  /* Tell the signal thread that we are doing an interruptible RPC on
 
104
     this port.  If we get a signal and should return EINTR, the signal
 
105
     thread will set this variable to MACH_PORT_NULL.  The RPC might
 
106
     return EINTR when some other thread gets a signal, in which case we
 
107
     want to restart our call.  */
 
108
  ss->intr_port = m->header.msgh_remote_port;
 
109
 
 
110
  /* A signal may arrive here, after intr_port is set, but before the
 
111
     mach_msg system call.  The signal handler might do an interruptible
 
112
     RPC, and clobber intr_port; then it would not be set properly when we
 
113
     actually did send the RPC, and a later signal wouldn't interrupt that
 
114
     RPC.  So, _hurd_setup_sighandler saves intr_port in the sigcontext,
 
115
     and sigreturn restores it.  */
 
116
 
 
117
 message:
 
118
 
 
119
  /* XXX
 
120
     At all points here (once SS->intr_port is set), the signal thread
 
121
     thinks we are "about to enter the syscall", and might mutate our
 
122
     return-value register.  This is bogus.
 
123
   */
 
124
 
 
125
  if (ss->cancel)
 
126
    {
 
127
      /* We have been cancelled.  Don't do an RPC at all.  */
 
128
      ss->intr_port = MACH_PORT_NULL;
 
129
      ss->cancel = 0;
 
130
      return EINTR;
 
131
    }
 
132
 
 
133
  /* Note that the signal trampoline code might modify our OPTION!  */
 
134
  err = INTR_MSG_TRAP (msg, option, send_size,
 
135
                       rcv_size, rcv_name, timeout, notify);
 
136
 
 
137
  switch (err)
 
138
    {
 
139
    case MACH_RCV_TIMED_OUT:
 
140
      if (user_option & MACH_RCV_TIMEOUT)
 
141
        /* The real user RPC timed out.  */
 
142
        break;
 
143
      else
 
144
        /* The operation was supposedly interrupted, but still has
 
145
           not returned.  Declare it interrupted.  */
 
146
        goto interrupted;
 
147
 
 
148
    case MACH_SEND_INTERRUPTED: /* RPC didn't get out.  */
 
149
      if (!(option & MACH_SEND_MSG))
 
150
        {
 
151
          /* Oh yes, it did!  Since we were not doing a message send,
 
152
             this return code cannot have come from the kernel!
 
153
             Instead, it was the signal thread mutating our state to tell
 
154
             us not to enter this RPC.  However, we are already in the receive!
 
155
             Since the signal thread thought we weren't in the RPC yet,
 
156
             it didn't do an interrupt_operation.
 
157
             XXX */
 
158
          goto retry_receive;
 
159
        }
 
160
      /* FALLTHROUGH */
 
161
 
 
162
      /* These are the other codes that mean a pseudo-receive modified
 
163
         the message buffer and we might need to clean up the port rights.  */
 
164
    case MACH_SEND_TIMED_OUT:
 
165
    case MACH_SEND_INVALID_NOTIFY:
 
166
#ifdef MACH_SEND_NO_NOTIFY
 
167
    case MACH_SEND_NO_NOTIFY:
 
168
#endif
 
169
#ifdef MACH_SEND_NOTIFY_IN_PROGRESS
 
170
    case MACH_SEND_NOTIFY_IN_PROGRESS:
 
171
#endif
 
172
      if (MACH_MSGH_BITS_REMOTE (msg->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND)
 
173
        {
 
174
          __mach_port_deallocate (__mach_task_self (), msg->msgh_remote_port);
 
175
          msg->msgh_bits
 
176
            = (MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
 
177
                               MACH_MSGH_BITS_LOCAL (msg->msgh_bits))
 
178
               | MACH_MSGH_BITS_OTHER (msg->msgh_bits));
 
179
        }
 
180
      if (msg->msgh_bits & MACH_MSGH_BITS_COMPLEX)
 
181
        {
 
182
#ifndef MACH_MSG_PORT_DESCRIPTOR
 
183
          /* Check for MOVE_SEND rights in the message.  These hold refs
 
184
             that we need to release in case the message is in fact never
 
185
             re-sent later.  Since it might in fact be re-sent, we turn
 
186
             these into COPY_SEND's after deallocating the extra user ref;
 
187
             the caller is responsible for still holding a ref to go with
 
188
             the original COPY_SEND right, so the resend copies it again.  */
 
189
 
 
190
          mach_msg_type_long_t *ty = (void *) (msg + 1);
 
191
          while ((void *) ty < (void *) msg + msg->msgh_size)
 
192
            {
 
193
              mach_msg_type_name_t name;
 
194
              mach_msg_type_size_t size;
 
195
              mach_msg_type_number_t number;
 
196
 
 
197
              inline void clean_ports (mach_port_t *ports, int dealloc)
 
198
                {
 
199
                  mach_msg_type_number_t i;
 
200
                  switch (name)
 
201
                    {
 
202
                    case MACH_MSG_TYPE_MOVE_SEND:
 
203
                      for (i = 0; i < number; i++)
 
204
                        __mach_port_deallocate (__mach_task_self (), *ports++);
 
205
                      if (ty->msgtl_header.msgt_longform)
 
206
                        ty->msgtl_name = MACH_MSG_TYPE_COPY_SEND;
 
207
                      else
 
208
                        ty->msgtl_header.msgt_name = MACH_MSG_TYPE_COPY_SEND;
 
209
                      break;
 
210
                    case MACH_MSG_TYPE_COPY_SEND:
 
211
                    case MACH_MSG_TYPE_MOVE_RECEIVE:
 
212
                      break;
 
213
                    default:
 
214
                      if (MACH_MSG_TYPE_PORT_ANY (name))
 
215
                        assert (! "unexpected port type in interruptible RPC");
 
216
                    }
 
217
                  if (dealloc)
 
218
                    __vm_deallocate (__mach_task_self (),
 
219
                                     (vm_address_t) ports,
 
220
                                     number * sizeof (mach_port_t));
 
221
                }
 
222
 
 
223
              if (ty->msgtl_header.msgt_longform)
 
224
                {
 
225
                  name = ty->msgtl_name;
 
226
                  size = ty->msgtl_size;
 
227
                  number = ty->msgtl_number;
 
228
                  ty = (void *) ty + sizeof (mach_msg_type_long_t);
 
229
                }
 
230
              else
 
231
                {
 
232
                  name = ty->msgtl_header.msgt_name;
 
233
                  size = ty->msgtl_header.msgt_size;
 
234
                  number = ty->msgtl_header.msgt_number;
 
235
                  ty = (void *) ty + sizeof (mach_msg_type_t);
 
236
                }
 
237
 
 
238
              if (ty->msgtl_header.msgt_inline)
 
239
                {
 
240
                  clean_ports ((void *) ty, 0);
 
241
                  /* calculate length of data in bytes, rounding up */
 
242
                  ty = (void *) ty + (((((number * size) + 7) >> 3)
 
243
                                       + sizeof (mach_msg_type_t) - 1)
 
244
                                      &~ (sizeof (mach_msg_type_t) - 1));
 
245
                }
 
246
              else
 
247
                {
 
248
                  clean_ports (*(void **) ty,
 
249
                               ty->msgtl_header.msgt_deallocate);
 
250
                  ty = (void *) ty + sizeof (void *);
 
251
                }
 
252
            }
 
253
#else  /* Untyped Mach IPC flavor. */
 
254
          mach_msg_body_t *body = (void *) (msg + 1);
 
255
          mach_msg_descriptor_t *desc = (void *) (body + 1);
 
256
          mach_msg_descriptor_t *desc_end = desc + body->msgh_descriptor_count;
 
257
          for (; desc < desc_end; ++desc)
 
258
            switch (desc->type.type)
 
259
              {
 
260
              case MACH_MSG_PORT_DESCRIPTOR:
 
261
                switch (desc->port.disposition)
 
262
                  {
 
263
                  case MACH_MSG_TYPE_MOVE_SEND:
 
264
                    __mach_port_deallocate (mach_task_self (),
 
265
                                            desc->port.name);
 
266
                    desc->port.disposition = MACH_MSG_TYPE_COPY_SEND;
 
267
                    break;
 
268
                  case MACH_MSG_TYPE_COPY_SEND:
 
269
                  case MACH_MSG_TYPE_MOVE_RECEIVE:
 
270
                    break;
 
271
                  default:
 
272
                    assert (! "unexpected port type in interruptible RPC");
 
273
                  }
 
274
                break;
 
275
              case MACH_MSG_OOL_DESCRIPTOR:
 
276
                if (desc->out_of_line.deallocate)
 
277
                  __vm_deallocate (__mach_task_self (),
 
278
                                   (vm_address_t) desc->out_of_line.address,
 
279
                                   desc->out_of_line.size);
 
280
                break;
 
281
              case MACH_MSG_OOL_PORTS_DESCRIPTOR:
 
282
                switch (desc->ool_ports.disposition)
 
283
                  {
 
284
                  case MACH_MSG_TYPE_MOVE_SEND:
 
285
                    {
 
286
                      mach_msg_size_t i;
 
287
                      const mach_port_t *ports = desc->ool_ports.address;
 
288
                      for (i = 0; i < desc->ool_ports.count; ++i)
 
289
                        __mach_port_deallocate (__mach_task_self (), ports[i]);
 
290
                      desc->ool_ports.disposition = MACH_MSG_TYPE_COPY_SEND;
 
291
                      break;
 
292
                    }
 
293
                  case MACH_MSG_TYPE_COPY_SEND:
 
294
                  case MACH_MSG_TYPE_MOVE_RECEIVE:
 
295
                    break;
 
296
                  default:
 
297
                    assert (! "unexpected port type in interruptible RPC");
 
298
                  }
 
299
                if (desc->ool_ports.deallocate)
 
300
                  __vm_deallocate (__mach_task_self (),
 
301
                                   (vm_address_t) desc->ool_ports.address,
 
302
                                   desc->ool_ports.count
 
303
                                   * sizeof (mach_port_t));
 
304
                break;
 
305
              default:
 
306
                assert (! "unexpected descriptor type in interruptible RPC");
 
307
              }
 
308
#endif
 
309
        }
 
310
      break;
 
311
 
 
312
    case EINTR:
 
313
      /* Either the process was stopped and continued,
 
314
         or the server doesn't support interrupt_operation.  */
 
315
      if (ss->intr_port != MACH_PORT_NULL)
 
316
        /* If this signal was for us and it should interrupt calls, the
 
317
           signal thread will have cleared SS->intr_port.
 
318
           Since it's not cleared, the signal was for another thread,
 
319
           or SA_RESTART is set.  Restart the interrupted call.  */
 
320
        {
 
321
          /* Make sure we have a valid reply port.  The one we were using
 
322
             may have been destroyed by interruption.  */
 
323
          m->header.msgh_local_port = rcv_name = __mig_get_reply_port ();
 
324
          m->header.msgh_bits = msgh_bits;
 
325
          option = user_option;
 
326
          timeout = user_timeout;
 
327
          goto message;
 
328
        }
 
329
      /* FALLTHROUGH */
 
330
 
 
331
    case MACH_RCV_PORT_DIED:
 
332
      /* Server didn't respond to interrupt_operation,
 
333
         so the signal thread destroyed the reply port.  */
 
334
      /* FALLTHROUGH */
 
335
 
 
336
    interrupted:
 
337
      err = EINTR;
 
338
 
 
339
      /* The EINTR return indicates cancellation, so clear the flag.  */
 
340
      ss->cancel = 0;
 
341
      break;
 
342
 
 
343
    case MACH_RCV_INTERRUPTED:  /* RPC sent; no reply.  */
 
344
      option &= ~MACH_SEND_MSG; /* Don't send again.  */
 
345
    retry_receive:
 
346
      if (ss->intr_port == MACH_PORT_NULL)
 
347
        {
 
348
          /* This signal or cancellation was for us.  We need to wait for
 
349
             the reply, but not hang forever.  */
 
350
          option |= MACH_RCV_TIMEOUT;
 
351
          /* Never decrease the user's timeout.  */
 
352
          if (!(user_option & MACH_RCV_TIMEOUT)
 
353
              || timeout > _hurd_interrupted_rpc_timeout)
 
354
            timeout = _hurd_interrupted_rpc_timeout;
 
355
        }
 
356
      else
 
357
        {
 
358
          option = user_option;
 
359
          timeout = user_timeout;
 
360
        }
 
361
      goto message;             /* Retry the receive.  */
 
362
 
 
363
    case MACH_MSG_SUCCESS:
 
364
      {
 
365
        /* We got a reply.  Was it EINTR?  */
 
366
#ifdef MACH_MSG_TYPE_BIT
 
367
        const union
 
368
        {
 
369
          mach_msg_type_t t;
 
370
          int i;
 
371
        } check =
 
372
          { t: { MACH_MSG_TYPE_INTEGER_T, sizeof (integer_t) * 8,
 
373
                 1, TRUE, FALSE, FALSE, 0 } };
 
374
#endif
 
375
 
 
376
        if (m->reply.RetCode == EINTR &&
 
377
            m->header.msgh_size == sizeof m->reply &&
 
378
#ifdef MACH_MSG_TYPE_BIT
 
379
            m->check.type == check.i &&
 
380
#endif
 
381
            !(m->header.msgh_bits & MACH_MSGH_BITS_COMPLEX))
 
382
          {
 
383
            /* It is indeed EINTR.  Is the interrupt for us?  */
 
384
            if (ss->intr_port != MACH_PORT_NULL)
 
385
              {
 
386
                /* Nope; repeat the RPC.
 
387
                   XXX Resources moved? */
 
388
 
 
389
                assert (m->header.msgh_id == msgid + 100);
 
390
 
 
391
                /* We know we have a valid reply port, because we just
 
392
                   received the EINTR reply on it.  Restore it and the
 
393
                   other fields in the message header needed for send,
 
394
                   since the header now reflects receipt of the reply.  */
 
395
                m->header.msgh_local_port = rcv_name;
 
396
                m->header.msgh_remote_port = remote_port;
 
397
                m->header.msgh_id = msgid;
 
398
                m->header.msgh_bits = msgh_bits;
 
399
                /* Restore the two words clobbered by the reply data.  */
 
400
                m->request.data = save_data;
 
401
 
 
402
                /* Restore the original mach_msg options.
 
403
                   OPTION may have had MACH_RCV_TIMEOUT added,
 
404
                   and/or MACH_SEND_MSG removed.  */
 
405
                option = user_option;
 
406
                timeout = user_timeout;
 
407
 
 
408
                /* Now we are ready to repeat the original message send.  */
 
409
                goto message;
 
410
              }
 
411
            else
 
412
              /* The EINTR return indicates cancellation,
 
413
                 so clear the flag.  */
 
414
              ss->cancel = 0;
 
415
          }
 
416
      }
 
417
      break;
 
418
 
 
419
    default:                    /* Quiet -Wswitch-enum.  */
 
420
      break;
 
421
    }
 
422
 
 
423
  ss->intr_port = MACH_PORT_NULL;
 
424
 
 
425
  return err;
 
426
}