2016-01-19 4 views
2

Я пытаюсь передать порт mach к дочернему процессу, созданному с fork на Mac OSX. Я видел этот SO вопрос Sharing Mach ports with child processes, но у него нет решения, которое просто описывает проблему. Глядя на этот сайт https://robert.sesek.com/2014/1/changes_to_xnu_mach_ipc.html, он содержит указания о передаче механических портов дочернему процессу, но, к сожалению, их код не является примером.Передача порта для дочернего процесса

Я попытался реализовать обмен портов, но дочерний процесс не может получить сообщение, отправленное родительским процессом, mach_msg внутри recv_port с ошибкой invalid name. Ниже я до сих пор. Извините за так много кода, мах IPC-типа затрудняет краткое описание.

Как я могу передать порт mach к дочернему процессу на Mac OSX теперь, когда взломать порт загрузочного порта больше не работает?

Редактировать

Я изменил пример кода, чтобы отразить те моменты, которые Кен Thomases сделал в своем ответе, дочерний процесс создает порт с посылом вправо и отправляет его родитель. Однако родительский процесс не может получить порт, созданный и отправленный дочерним элементом, и просто зависает на recv_port.

#include <stdio.h> 
#include <mach/mach.h> 
#include <mach/error.h> 
#include <mach/message.h> 
#include <unistd.h> 

static int32_t 
send_port(mach_port_t remote_port, mach_port_t port) 
{ 
    kern_return_t err; 

    struct 
    { 
     mach_msg_header_t   header; 
     mach_msg_body_t   body; 
     mach_msg_port_descriptor_t task_port; 
    } msg; 

    msg.header.msgh_remote_port = remote_port; 
    msg.header.msgh_local_port = MACH_PORT_NULL; 
    msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0) | 
     MACH_MSGH_BITS_COMPLEX; 
    msg.header.msgh_size = sizeof msg; 

    msg.body.msgh_descriptor_count = 1; 
    msg.task_port.name = port; 
    msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND; 
    msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR; 

    err = mach_msg_send(&msg.header); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't send mach msg\n", err); 
     return (-1); 
    } 

    return (0); 
} 

static int32_t 
recv_port(mach_port_t recv_port, mach_port_t *port) 
{ 
    kern_return_t err; 
    struct 
    { 
     mach_msg_header_t   header; 
     mach_msg_body_t   body; 
     mach_msg_port_descriptor_t task_port; 
     mach_msg_trailer_t   trailer; 
    } msg; 

    err = mach_msg(&msg.header, MACH_RCV_MSG, 
        0, sizeof msg, recv_port, 
        MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't recieve mach message\n", err); 
     return (-1); 
    } 

    *port = msg.task_port.name; 
    return 0; 
} 

static int32_t 
setup_recv_port(mach_port_t *recv_port) 
{ 
    kern_return_t  err; 
    mach_port_t   port = MACH_PORT_NULL; 
    err = mach_port_allocate(mach_task_self(), 
           MACH_PORT_RIGHT_RECEIVE, &port); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't allocate mach port\n", err); 
     return (-1); 
    } 

    err = mach_port_insert_right(mach_task_self(), 
            port, 
            port, 
            MACH_MSG_TYPE_MAKE_SEND); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't insert port right\n", err); 
     return (-1); 
    } 

    (*recv_port) = port; 
    return (0); 
} 

pid_t 
fork_pass_port(mach_port_t pass_port, int32_t (*child_start)(mach_port_t port, void *arg), void *arg) 
{ 
    pid_t pid = 0; 
    int32_t rtrn = 0; 
    kern_return_t err; 
    mach_port_t special_port = MACH_PORT_NULL; 

    /* Setup the mach port. */ 
    if(setup_recv_port(&pass_port) != 0) 
    { 
     printf("Can't setup mach port\n"); 
     return (-1); 
    } 

    /* Grab our current task's(process's) HOST_NAME special port. */ 
    err = task_get_special_port(mach_task_self(), TASK_HOST_PORT, &special_port); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't get special port:\n", err); 
     return (-1); 
    } 

    /* Set the HOST_NAME special port as the parent recv port. */ 
    err = task_set_special_port(mach_task_self(), TASK_HOST_PORT, pass_port); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't set special port:\n", err); 
     return (-1); 
    } 

    pid = fork(); 
    if(pid == 0) 
    { 
     mach_port_t host_port = MACH_PORT_NULL; 
     mach_port_t port = MACH_PORT_NULL; 

     /* In the child process grab the port passed by the parent. */ 
     err = task_get_special_port(mach_task_self(), TASK_HOST_PORT, &pass_port); 
     if(err != KERN_SUCCESS) 
     { 
      mach_error("Can't get special port:\n", err); 
      return (-1); 
     } 

     /* Create a port with a send right. */ 
     if(setup_recv_port(&port) != 0) 
     { 
      printf("Can't setup mach port\n"); 
      return (-1); 
     } 

     /* Send port to parent. */ 
     rtrn = send_port(pass_port, port); 
     if(rtrn < 0) 
     { 
      printf("Can't send port\n"); 
      return (-1); 
     } 

     /* Now that were done passing the mach port, start the function passed by the caller. */ 
     child_start(pass_port, arg); 

     /* Exit and clean up the child process. */ 
     _exit(0); 
    } 
    else if(pid > 0) 
    { 
     mach_port_t child_port = MACH_PORT_NULL; 

     rtrn = recv_port(pass_port, &child_port); 
     if(rtrn < 0) 
     { 
      printf("Can't recv port\n"); 
      return (-1); 
     } 

     /* Reset parents special port. */ 
     err = task_set_special_port(mach_task_self(), TASK_HOST_PORT, special_port); 
     if(err != KERN_SUCCESS) 
     { 
      mach_error("Can't set special port:\n", err); 
      return (-1); 
     } 

     return (0); 
    } 
    else 
    { 
     /* Error, so cleanup the mach port. */ 
     err = mach_port_deallocate(mach_task_self(), pass_port); 
     if(err != KERN_SUCCESS) 
     { 
      mach_error("Can't deallocate mach port\n", err); 
      return (-1); 
     } 

     perror("fork"); 

     return (-1); 
    } 
} 

static int32_t start(mach_port_t port, void *arg) 
{ 
    printf("Started\n"); 

    return (0); 
} 

int main(void) 
{ 
    char *arg = "argument"; 
    mach_port_t port = MACH_PORT_NULL; 

    pid_t pid = fork_pass_port(port, start, arg); 
    if(pid < 0) 
    { 
     printf("Can't fork and pass msg port\n"); 
     return (-1); 
    } 

    return (0); 
} 
+0

Это Q & A любая помощь? http://stackoverflow.com/questions/8481138/how-to-use-sendmsg-to-send-a-file-descriptor-via-sockets-between-2-processes –

+0

@John Hascall К сожалению, это не так, я необходимо использовать обмен сообщениями OOL между процессами. – 2trill2spill

+0

Я думаю, что проблема заключается в попытке использовать специальный хост-порт для наследования. Либо ребенок неправильно наследует хост-порт родителя, либо ядро ​​каким-то образом вмешивается в его использование для отправки сообщения. Если я использую ваш код для отправки сообщения (включая правый порт) в рамках одного процесса, он отлично работает. –

ответ

3

Я понял, как передать порты Mach через специальное наследование портов. Вы должны временно заменить TASK_BOOTSTRAP_PORT на порт, который хотите передать, перед вызовом fork. Другие специальные порты так или иначе терпят неудачу. Ниже приведен пример «тангажа для обмена портами».

Примечание: этот код был проверен только на OSX 10.11.3 и может не работать с предыдущими или будущими версиями OSX.

#include <stdio.h> 
#include <mach/mach.h> 
#include <mach/error.h> 
#include <mach/message.h> 
#include <unistd.h> 

#define SPECIAL_PORT TASK_BOOTSTRAP_PORT 

static int32_t 
send_port(mach_port_t remote_port, mach_port_t port) 
{ 
    kern_return_t err; 

    struct 
    { 
     mach_msg_header_t   header; 
     mach_msg_body_t   body; 
     mach_msg_port_descriptor_t task_port; 
    } msg; 

    msg.header.msgh_remote_port = remote_port; 
    msg.header.msgh_local_port = MACH_PORT_NULL; 
    msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0) | 
     MACH_MSGH_BITS_COMPLEX; 
    msg.header.msgh_size = sizeof msg; 

    msg.body.msgh_descriptor_count = 1; 
    msg.task_port.name = port; 
    msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND; 
    msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR; 

    err = mach_msg_send(&msg.header); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't send mach msg\n", err); 
     return (-1); 
    } 

    return (0); 
} 

static int32_t 
recv_port(mach_port_t recv_port, mach_port_t *port) 
{ 
    kern_return_t err; 
    struct 
    { 
     mach_msg_header_t   header; 
     mach_msg_body_t   body; 
     mach_msg_port_descriptor_t task_port; 
     mach_msg_trailer_t   trailer; 
    } msg; 

    err = mach_msg(&msg.header, MACH_RCV_MSG, 
        0, sizeof msg, recv_port, 
        MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't recieve mach message\n", err); 
     return (-1); 
    } 

    (*port) = msg.task_port.name; 
    return 0; 
} 

static int32_t 
setup_recv_port(mach_port_t *recv_port) 
{ 
    kern_return_t  err; 
    mach_port_t   port = MACH_PORT_NULL; 
    err = mach_port_allocate(mach_task_self(), 
           MACH_PORT_RIGHT_RECEIVE, &port); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't allocate mach port\n", err); 
     return (-1); 
    } 

    err = mach_port_insert_right(mach_task_self(), 
            port, 
            port, 
            MACH_MSG_TYPE_MAKE_SEND); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't insert port right\n", err); 
     return (-1); 
    } 

    (*recv_port) = port; 
    return (0); 
} 

static int32_t 
start(mach_port_t port, void *arg) 
{ 

    return (0); 
} 

static pid_t 
fork_pass_port(mach_port_t *pass_port, 
       int32_t (*child_start)(mach_port_t port, void *arg), 
       void *arg) 
{ 
    pid_t pid = 0; 
    int32_t rtrn = 0; 
    kern_return_t err; 
    mach_port_t special_port = MACH_PORT_NULL; 

    /* Allocate the mach port. */ 
    if(setup_recv_port(pass_port) != 0) 
    { 
     printf("Can't setup mach port\n"); 
     return (-1); 
    } 

    /* Grab our current process's bootstrap port. */ 
    err = task_get_special_port(mach_task_self(), SPECIAL_PORT, &special_port); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't get special port:\n", err); 
     return (-1); 
    } 

    /* Set the special port as the parent recv port. */ 
    err = task_set_special_port(mach_task_self(), SPECIAL_PORT, (*pass_port)); 
    if(err != KERN_SUCCESS) 
    { 
     mach_error("Can't set special port:\n", err); 
     return (-1); 
    } 

    pid = fork(); 
    if(pid == 0) 
    { 
     mach_port_t bootstrap_port = MACH_PORT_NULL; 
     mach_port_t port = MACH_PORT_NULL; 

     /* In the child process grab the port passed by the parent. */ 
     err = task_get_special_port(mach_task_self(), SPECIAL_PORT, pass_port); 
     if(err != KERN_SUCCESS) 
     { 
      mach_error("Can't get special port:\n", err); 
      return (-1); 
     } 

     /* Create a port with a send right. */ 
     if(setup_recv_port(&port) != 0) 
     { 
      printf("Can't setup mach port\n"); 
      return (-1); 
     } 

     /* Send port to parent. */ 
     rtrn = send_port((*pass_port), port); 
     if(rtrn < 0) 
     { 
      printf("Can't send port\n"); 
      return (-1); 
     } 

     /* Receive the real bootstrap port from the parent. */ 
     rtrn = recv_port(port, &bootstrap_port); 
     if(rtrn < 0) 
     { 
      printf("Can't receive bootstrap port\n"); 
      return (-1); 
     } 

     /* Set the bootstrap port back to normal. */ 
     err = task_set_special_port(mach_task_self(), SPECIAL_PORT, bootstrap_port); 
     if(err != KERN_SUCCESS) 
     { 
      mach_error("Can't set special port:\n", err); 
      return (-1); 
     } 

     /* Now that were done with the port dance, start the function passed by the caller. */ 
     child_start((*pass_port), arg); 

     /* Exit and clean up the child process. */ 
     _exit(0); 
    } 
    else if(pid > 0) 
    { 
     mach_port_t child_port = MACH_PORT_NULL; 

     /* Grab the child's recv port. */ 
     rtrn = recv_port((*pass_port), &child_port); 
     if(rtrn < 0) 
     { 
      printf("Can't recv port\n"); 
      return (-1); 
     } 

     /* Send the child the original bootstrap port. */ 
     rtrn = send_port(child_port, special_port); 
     if(rtrn < 0) 
     { 
      printf("Can't send bootstrap port\n"); 
      return (-1); 
     } 

     /* Reset parents special port. */ 
     err = task_set_special_port(mach_task_self(), SPECIAL_PORT, special_port); 
     if(err != KERN_SUCCESS) 
     { 
      mach_error("Can't set special port:\n", err); 
      return (-1); 
     } 

     return (0); 
    } 
    else 
    { 
     /* Error, so cleanup the mach port. */ 
     err = mach_port_deallocate(mach_task_self(), (*pass_port)); 
     if(err != KERN_SUCCESS) 
     { 
      mach_error("Can't deallocate mach port\n", err); 
      return (-1); 
     } 

     perror("fork"); 

     return (-1); 
    } 
} 

int main(void) 
{ 
    /* Argument to pass to the child process. */ 
    char *arg = "argument"; 

    /* Mach port we want to pass to the child. */ 
    mach_port_t port = MACH_PORT_NULL; 

    pid_t pid = fork_pass_port(&port, start, arg); 
    if(pid < 0) 
    { 
     printf("Can't fork and pass msg port\n"); 
     return (-1); 
    } 

    return (0); 
} 
+0

Привет, В настоящее время я имею дело с аналогичной проблемой, с тем, что дочерний процесс отправляет mach_port из пространства ядра (из-за точности команды драйвера iokit), и он должен быть получен в слое пользовательского пространства родительского процесса .. знаете ли вы, что нужно сделать, чтобы заставить ваш код работать над этим сценарием? спасибо – osxUser

+0

@osxUser Извините за поздний ответ, но вы должны быть в состоянии отправить mach_port из ядра в пространство пользователя. Вместо того, чтобы использовать fork(), вам нужно знать pid процесса, который вы хотите отправить, и использовать task_for_pid(), чтобы получить mach_task, тогда вы можете использовать mach_port_names(), чтобы получить доступные порты mach в задаче. Надеюсь, это поможет. – 2trill2spill

2

Только один человек получает право на любой порт. Родитель имеет право на получение создаваемого порта. Наследование специальных портов предназначено только для прав на передачу. Таким образом, ребенок наследует только право на передачу вашего коммуникационного порта.

В статье, в которой вы ссылаетесь, говорится, что ребенок отправляет сообщение родительскому над этим портом. Ребенок должен был создать свой собственный новый порт с правом на получение. Его сообщение будет иметь право на отправку или передачу для этого порта родительскому, в зависимости от того, нужна ли вам непрерывная двусторонняя связь. Ребенок отправит это сообщение (-once) прямо в сообщение msgh_local_port; родитель получит его в msgh_remote_port. Родитель может ответить, используя это право send (-once), и ответ может переносить право отправки на исходный хост-порт. Ребенок может использовать это для восстановления своего хост-порта.

В качестве альтернативы, вы можете быть в состоянии сделать это:

  • проход к ребенку, через специальный порт наследования, право на отправку для задачи порта родителя; с этим, ребенок может сделать почти ничего, чтобы родитель
  • есть ребенок экстрагирования получить право вашего коммуникационного порта от родителя с помощью mach_port_extract_right()
  • есть она освобождает его посыл право порта задачи родительской, только для безопасности

Он также может извлекать право на отправку исходного порта хоста, а не получать его через IPC, что должно быть проще.

Все это говорит о том, что заставляет вас думать, что порт хоста более безопасен, чем порт бутстрапа, чтобы использовать этот способ?

+0

Использование порта бутстрапа зависает систем после Lion, я думаю, что он разбивает XPC, и так думает проект Chromium для использования IPC для Chrome на Mac OSX. – 2trill2spill

+0

Я понимаю, что есть проблемы с портом bootstrap. Я предполагал, что вы так же столкнетесь с проблемами с портом хоста. Без официального обещания Apple, что это безопасно и поддерживается, вы играете с огнем. –

Смежные вопросы