etc.git


etc

~ scripts and random things
git clone https://github.com/djnnvx/etc.git
log | files | refs | archive

fuzz_pdu.c (2165B)

      1 /*
      2  * RPC framing layer. Sits above the per-procedure zdr decoders, below the
      3  * raw socket read. libnfs_zdr_replymsg parses the rpc_msg envelope (xid,
      4  * direction, accepted-vs-rejected reply, auth verifier flavour, status).
      5  *
      6  * Reachable on every reply from a malicious server, before any
      7  * NFS-procedure-specific code runs. Bugs here break confinement universally.
      8  * libnfs_zdr_callmsg sits on the server-side parse path; rarer but exists
      9  * (servers/rpcbind, etc.).
     10  *
     11  * The harness lets the first byte pick the direction (CALL vs REPLY) so a
     12  * single corpus hits both framings.
     13  */
     14 #include "harness.h"
     15 /* libnfs.h has to come first - it defines the EXTERN export macro that
     16  * libnfs-raw.h depends on but does not itself provide. */
     17 #include "nfsc/libnfs.h"
     18 #include "nfsc/libnfs-raw.h"
     19 
     20 static int fuzz_one(const uint8_t *data, size_t size)
     21 {
     22     if (size < 1)
     23         return 0;
     24     int as_call = (data[0] & 1);
     25     const uint8_t *payload = data + 1;
     26     size_t payload_size = size - 1;
     27 
     28     ZDR zdr;
     29     struct rpc_msg msg;
     30     nfs_zdrmem_decode(&zdr, payload, payload_size);
     31     memset(&msg, 0, sizeof(msg));
     32 
     33     /* libnfs_accepted_reply (zdr.c:534) dispatches on status=SUCCESS by
     34      * calling msg->body.rbody.reply.areply.reply_data.results.proc which is
     35      * normally set by rpc_allocate_pdu when sending the request. In a stateless
     36      * fuzzer we have no pending PDU; plug in the no-op decoder so the SUCCESS
     37      * branch parses without dereferencing a NULL fn ptr. */
     38     msg.body.rbody.reply.areply.reply_data.results.proc =
     39         (zdrproc_t)libnfs_zdr_void;
     40     msg.body.rbody.reply.areply.reply_data.results.where = NULL;
     41 
     42     struct rpc_context *rpc = rpc_init_context();
     43     if (!rpc) {
     44         zdr_destroy(&zdr);
     45         return 0;
     46     }
     47 
     48     if (as_call) {
     49         if (libnfs_zdr_callmsg(rpc, &zdr, &msg)) {
     50             /* nothing to free - the call body's auth fields aren't owned. */
     51         }
     52     } else {
     53         if (libnfs_zdr_replymsg(rpc, &zdr, &msg)) {
     54             /* same: reply envelope doesn't own its sub-buffers in this layer. */
     55         }
     56     }
     57 
     58     zdr_destroy(&zdr);
     59     rpc_destroy_context(rpc);
     60     return 0;
     61 }