Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/trusty/trusty/user/base/lib/tipc/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 13 kB image not shown  

Quelle  tipc_srv.c

  Sprache: C
 

/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#define TLOG_TAG "tipc-srv"

#include <lib/tipc/tipc_srv.h>

#include <assert.h>
#include <interface/line-coverage/aggregator.h>
#include <inttypes.h>
#include <lib/coverage/common/cov_shm.h>
#include <lib/coverage/common/ipc.h>
#include <lib/line-coverage/shm.h>
#include <lk/err_ptr.h>
#include <lk/list.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <trusty_log.h>
#include <uapi/err.h>
#include <uapi/mm.h>

#include "tipc_priv.h"

static void port_event_handler_proc(const struct uevent* ev,
                                    void* tipc_port_ctx);
static void chan_event_handler_proc(const struct uevent* ev, void* chan_ctx);

struct tipc_port_ctx {
    struct tipc_event_handler event_handler;
    const struct tipc_port* cfg;
    struct tipc_srv* srv;
    handle_t handle;
};

struct tipc_chan_ctx {
    struct list_node chan_list_node;
    struct tipc_event_handler event_handler;
    struct tipc_port_ctx* port;
    handle_t handle;
    void* user_ctx;
};

struct tipc_srv {
    struct list_node chan_list;
    const struct tipc_srv_ops* ops;
    struct tipc_hset* hset;
    uint32_t chan_cnt;
    uint32_t max_chan_cnt;
    uint32_t port_cnt;
    struct tipc_port_ctx ports[0];
};

/*
 * Helper to mask/unmask events for all ports
 */

static void set_ports_event_mask(struct tipc_srv* srv, uint32_t mask) {
    uint32_t i;
    struct tipc_port_ctx* p;

    /* unmask ports here */
    for (i = 0, p = srv->ports; i < srv->port_cnt; i++, p++) {
        (void)tipc_hset_mod_entry(srv->hset, p->handle, mask,
                                  &p->event_handler);
    }
}

static bool server_at_max_chan_cnt(struct tipc_srv* srv) {
    return (srv->max_chan_cnt && (srv->chan_cnt == srv->max_chan_cnt));
}

/*
 * Helper to close channel
 */

static void tipc_chan_close(struct tipc_chan_ctx* chan) {
    int rc;
    struct tipc_srv* srv = chan->port->srv;
    void* user_ctx = chan->user_ctx;

    /* remove it from handle set */
    rc = tipc_hset_remove_entry(srv->hset, chan->handle);
    if (rc != NO_ERROR) {
        /* the only reason for this to fail if any handle is somehow
         * becomes invalid. There is no reasonable way to recover
         * from this.
         */

        TLOGE("hset_remove_entry failed (%d)\n", rc);
        abort();
    }

    /* remove it from list */
    list_delete(&chan->chan_list_node);

    /*
     * if we had  a maximum number of channels we will now be below maximum.
     * Unmask ports for this service so we can create channels.
     */

    if (server_at_max_chan_cnt(srv)) {
        set_ports_event_mask(srv, ~0u);
    }

    /* decrement channel count */
    srv->chan_cnt--;

    /* close channel */
    close(chan->handle);

    /* free memory */
    free(chan);

    /*  cleanup user allocated state if any */
    if (srv->ops->on_channel_cleanup && user_ctx) {
        srv->ops->on_channel_cleanup(user_ctx);
    }
}

/*
 *  channel event handler
 */

static void chan_event_handler_proc(const struct uevent* ev, void* chan_ctx) {
    int rc;
    struct tipc_chan_ctx* chan = chan_ctx;
    struct tipc_srv* srv = chan->port->srv;

    assert(ev->handle == chan->handle);

    tipc_handle_chan_errors(ev);

    setup_shm();

    if (ev->event & IPC_HANDLE_POLL_MSG) {
        rc = srv->ops->on_message(chan->port->cfg, chan->handle,
                                  chan->user_ctx);
        if (rc < 0) {
            /* report an error and close channel */
            TLOGE("failed (%d) to handle event on channel %d\n", rc,
                  ev->handle);
            tipc_chan_close(chan);
            dump_shm();
            return;
        }
    }

    if (ev->event & IPC_HANDLE_POLL_HUP) {
        /* closed by peer. */
        TLOGD("close connection\n");
        dump_shm();
        if (srv->ops->on_disconnect) {
            srv->ops->on_disconnect(chan->port->cfg, chan->handle,
                                    chan->user_ctx);
        }

        tipc_chan_close(chan);
        return;
    }

    if (ev->event & IPC_HANDLE_POLL_SEND_UNBLOCKED) {
        if (srv->ops->on_send_unblocked) {
            rc = srv->ops->on_send_unblocked(chan->port->cfg, chan->handle,
                                             chan->user_ctx);
            if (rc < 0) {
                /* report an error and close channel */
                TLOGE("failed (%d) to handle event on channel %d\n", rc,
                      ev->handle);
                tipc_chan_close(chan);
                return;
            }
        } else {
            TLOGE("send-unblocking not handled for channel %d\n", ev->handle);
            tipc_chan_close(chan);
            return;
        }
    }
}

/*
 *  Check if client is allowed to connect on specified port
 */

static bool client_is_allowed(const struct tipc_port_acl* acl,
                              const struct uuid* peer) {
    uint32_t i;

    if (!acl->uuid_num)
        return true;

    for (i = 0; i < acl->uuid_num; i++) {
        if (memcmp(peer, acl->uuids[i], sizeof(*peer)) == 0) {
            /* match */
            return true;
        }
    }

    return false;
}

/*
 *  Handle incoming connection
 */

static void handle_connect(struct tipc_port_ctx* port) {
    int rc;
    handle_t hchan;
    struct trusty_peer_id_uuid peer = {
            .kind = TRUSTY_PEER_ID_KIND_UUID,
    };
    struct tipc_srv* srv = port->srv;

    TLOGD("Incoming connection on %s\n", port->cfg->name);

    /* incoming connection: accept it */
    rc = accept(port->handle, &peer.id);
    if (rc < 0) {
        TLOGE("failed (%d) to accept on port %s\n", rc, port->cfg->name);
        goto err_accept;
    }
    hchan = (handle_t)rc;

    if (server_at_max_chan_cnt(srv)) {
        /* we should not ever get here after we implement port mask */
        TLOGE("too many channels for port %s\n", port->cfg->name);
        goto err_too_many_chan;
    }

    /* do access control */
    if (!client_is_allowed(port->cfg->acl, &peer.id)) {
        TLOGE("access denied on port %s\n", port->cfg->name);
        goto err_access;
    }

    rc = tipc_add_connection(port, hchan, (struct trusty_peer_id*)&peer,
                             sizeof(peer));
    if (rc < 0) {
        goto err_tipc_add_connection;
    }
    return;

err_tipc_add_connection:
err_access:
err_too_many_chan:
    close(hchan);
err_accept:
    return;
}

/*
 * Add a new connection
 */

int tipc_add_connection(struct tipc_port_ctx* port,
                        handle_t hchan,
                        const struct trusty_peer_id* peer,
                        size_t peer_len) {
    int rc;
    void* user_ctx = NULL;
    struct tipc_chan_ctx* chan;
    struct tipc_srv* srv = port->srv;

    chan = calloc(1sizeof(*chan));
    if (!chan) {
        TLOGE("oom while handling port %s\n", port->cfg->name);
        rc = ERR_NO_MEMORY;
        goto err_oom;
    }

    /* fill channel structure */
    chan->event_handler.proc = chan_event_handler_proc;
    chan->event_handler.priv = chan;
    chan->port = port;
    chan->handle = hchan;

    /* add new channel to handle set */
    rc = tipc_hset_add_entry(srv->hset, hchan, ~0u, &chan->event_handler);
    if (rc != NO_ERROR) {
        TLOGE("failed (%d) to add chan to hset\n", rc);
        goto err_hset_add;
    }

    /* invoke on_connect handler if any */
    if (srv->ops->on_connect) {
        if (peer->kind != TRUSTY_PEER_ID_KIND_UUID) {
            TLOGE("unsupported peer kind %" PRIx64
                  " passed to on_connect on port %s. "
                  "Did you mean to implement on_connect_peer_id?\n",
                  peer->kind, port->cfg->name);
            goto err_unwrap_uuid_peer;
        }
        if (peer_len != sizeof(struct trusty_peer_id_uuid)) {
            TLOGE("unexpected peer_len %zu (expected %zu) passed to on_connect "
                  "for kind %" PRIx64
                  " (port %s). Do kernel and userspace have mismatched "
                  "versions?\n",
                  peer_len, sizeof(struct trusty_peer_id_uuid), peer->kind,
                  port->cfg->name);
            goto err_unwrap_uuid_peer;
        }

        rc = srv->ops->on_connect(port->cfg, chan->handle,
                                  (struct uuid*)&peer->data, &user_ctx);
        if (rc < 0) {
            TLOGE("on_connect failed (%d) on port %s\n", rc, port->cfg->name);
            goto err_on_connect;
        }
    }
    if (srv->ops->on_connect_peer_id) {
        rc = srv->ops->on_connect_peer_id(port->cfg, chan->handle, peer,
                                          peer_len, &user_ctx);
        if (rc < 0) {
            TLOGE("on_connect_peer_id failed (%d) on port %s\n", rc,
                  port->cfg->name);
            goto err_on_connect;
        }
    }

    /* attach context provided by caller */
    chan->user_ctx = user_ctx;

    /* add it to the list */
    list_add_tail(&srv->chan_list, &chan->chan_list_node);
    srv->chan_cnt++;

    /* mask all ports if max number of connections has been reached */
    if (server_at_max_chan_cnt(srv)) {
        set_ports_event_mask(srv, 0u);
    }

    TLOGD("got connection on %s\n", port->cfg->name);
    return NO_ERROR;

err_on_connect:
err_unwrap_uuid_peer:
err_hset_add:
    free(chan);
err_oom:
    return rc;
}

/*
 *  Port event handler
 */

static void port_event_handler_proc(const struct uevent* ev, void* ctx) {
    tipc_handle_port_errors(ev);

    if (ev->event & IPC_HANDLE_POLL_READY) {
        struct tipc_port_ctx* port = ctx;
        assert(port->handle == ev->handle);
        handle_connect(port);
    }
}

/*
 * Validate trusty service operations.
 *
 * Trusty service operations must satisfy the following requirements:
 * - service operations is a required parameter and must not be NULL
 * - on_message callback is mandatory and must be implemented by the service
 *
 * This function validates the above service operations requirements are met
 * by the passed service operation structure, and returns true; otherwise, it
 * returns false.
 */

static bool is_valid_tipc_srv_ops(const struct tipc_srv_ops* ops) {
    if (!ops) {
        TLOGE("Required service specific operations is NULL\n");
        return false;
    }

    if (!ops->on_message) {
        TLOGE("on_message callback is mandatory and cannot be NULL\n");
        return false;
    }

    if (ops->on_connect && ops->on_connect_peer_id) {
        TLOGE("Setting both on_connect and on_connect_peer_id is not "
              "allowed\n");
        return false;
    }

    return true;
}

/*
 *  Add new TIPC service to handle set
 */

int tipc_add_service_ports(struct tipc_hset* hset,
                           const struct tipc_port* ports,
                           uint32_t num_ports,
                           uint32_t max_chan_cnt,
                           const struct tipc_srv_ops* ops,
                           struct tipc_port_ctx** ports_out) {
    int rc;
    uint32_t i;
    struct tipc_srv* srv;
    struct tipc_port_ctx* port;

    if (IS_ERR(hset)) {
        TLOGE("invalid handle set (%d)\n", PTR_ERR(hset));
        return ERR_INVALID_ARGS;
    }

    if (!hset || !ports || !num_ports) {
        TLOGE("required parameter is missing\n");
        return ERR_INVALID_ARGS;
    }

    if (!is_valid_tipc_srv_ops(ops)) {
        TLOGE("Service operations failed validation\n");
        return ERR_INVALID_ARGS;
    }

    /* allocate new service */
    srv = calloc(1sizeof(struct tipc_srv) +
                            sizeof(struct tipc_port_ctx) * num_ports);
    if (!srv) {
        return ERR_NO_MEMORY;
    }

    /* and initialize it */
    srv->hset = hset;
    srv->port_cnt = num_ports;
    srv->max_chan_cnt = max_chan_cnt;

    list_initialize(&srv->chan_list);

    srv->ops = ops;
    for (i = 0; i < num_ports; i++) {
        srv->ports[i].handle = INVALID_IPC_HANDLE;
    }

    /* for each port */
    for (i = 0; i < num_ports; i++) {
        TLOGD("Initialize port: %s\n", ports[i].name);

        port = &srv->ports[i];

        if (!ports[i].acl) {
            TLOGE("ACL is required to create port\n");
            rc = ERR_INVALID_ARGS;
            goto err_no_acl;
        }

        /* create port */
        rc = port_create(ports[i].name, ports[i].msg_queue_len,
                         ports[i].msg_max_size, ports[i].acl->flags);
        if (rc < 0) {
            TLOGE("failed (%d) to create port\n", rc);
            goto err_port_create;
        }
        port->handle = (handle_t)rc;

        /* init event handler and other pointers */
        port->cfg = &ports[i];
        port->event_handler.proc = port_event_handler_proc;
        port->event_handler.priv = port;
        port->srv = srv;

        /* and add it to the handle set */
        rc = tipc_hset_add_entry(hset, port->handle, ~0u, &port->event_handler);
        if (rc < 0) {
            TLOGE("failed (%d) to register port\n", rc);
            goto err_hset_add;
        }
    }
    setup_mailbox(ports, num_ports);
    if (ports_out) {
        for (i = 0; i < num_ports; i++) {
            ports_out[i] = &srv->ports[i];
        }
    }
    return 0;

err_hset_add:
err_port_create:
err_no_acl:
    /* kill all ports we have created so far */
    for (i = 0; i < num_ports; i++) {
        if (srv->ports[i].handle != INVALID_IPC_HANDLE) {
            /* Note: closing handle also removes it from all handle sets */
            rc = close(srv->ports[i].handle);
            assert(rc == 0);
        }
    }
    /* then free service */
    free(srv);

    return rc;
}

struct tipc_hset* tipc_get_hset(struct tipc_port_ctx* port) {
    return port->srv->hset;
}

Messung V0.5 in Prozent
C=93 H=95 G=93

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-06-26) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.