3

forkMac OSX で作成された子プロセスにマッハ ポートを渡そうとしています。この SO の質問Shared Mach ports with child processesを見ましたが、問題を説明するだけの解決策はありません。このサイトhttps://robert.sesek.com/2014/1/changes_to_xnu_mach_ipc.htmlを見ると、マッハ ポートを子プロセスに渡す方法が含まれていますが、残念ながらサンプル コードではありません。

ポートスワップを実装しようとしましたが、子プロセスは親プロセスから送信されたメッセージを受信できmach_msgませrecv_portinvalid name。以下は私がこれまでに持っているものです。非常に多くのコードで申し訳ありませんが、mach IPC のようなものは簡潔にするのが難しくなります。

では、ブートストラップ ポート ハックが機能しなくなったので、Mac OSX の子プロセスにマッハ ポートを渡すにはどうすればよいでしょうか。

編集

Ken 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);
}
4

2 に答える 2

4

特別なポートの継承を介してマッハポートを渡す方法を見つけました。TASK_BOOTSTRAP_PORTfork を呼び出す前に、一時的に渡したいポートに置き換える必要があります。他の特別なポートは何らかの形で失敗します。以下は、「ポート スワップ ダンス」の例です。

このコードは 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);
}
于 2016-02-17T03:47:02.913 に答える
3

任意のポートに受信権は 1 つしかありません。親には、作成したポートの受信権があります。特別なポートの継承は、送信権のみを対象としています。したがって、子は通信ポートの送信権のみを継承します。

あなたがリンクした記事が示唆しているのは、子がこのポートを介して親にメッセージを送信することです。子は、受信権を持つ独自の新しいポートを作成する必要があります。そのメッセージは、進行中の二重通信が必要かどうかに応じて、そのポートの送信権または一度だけ送信する権利を親に伝えます。子はその send(-once) をmsgh_local_portメッセージの右側に配置します。親は でそれを受け取りますmsgh_remote_port。親はその send(-once) 権を使用して応答でき、応答は元のホスト ポートへの送信権を運ぶことができます。子はそれを使用して、ホスト ポートを復元できます。

または、これを行うことができる場合があります。

  • 特別なポート継承を介して、親のタスクポートの送信権を子に渡します。これにより、子供は親に対してほとんど何でもできます
  • を使用して、子に親から通信ポートの受信権を抽出させますmach_port_extract_right()
  • 安全のために、親タスク ポートの送信権の割り当てを解除します。

また、IPC を介して受信するのではなく、元のホスト ポートの送信権を抽出することもできます。これはより単純なはずです。

とはいえ、このように使用する場合、ブートストラップ ポートよりもホスト ポートの方が安全だと思われる理由は何ですか?

于 2016-01-19T04:08:01.100 に答える