rgb-cln/wire/wire_io.c

147 lines
3.7 KiB
C
Raw Normal View History

/* FIXME: io_plan needs size_t */
#include <unistd.h>
#include <ccan/io/io_plan.h>
2017-05-19 13:06:44 +01:00
#include <ccan/mem/mem.h>
#include <ccan/take/take.h>
#include <ccan/tal/tal.h>
#include <common/utils.h>
#include <errno.h>
#include <wire/wire_io.h>
/*
* OK, this is a little tricky. ccan/io lets you create your own plans,
* beyond the standard io_read/io_write etc. It provides a union to place
* scratch data, and it's almost enough for our purposes.
*/
/* 4 bytes for the length header. */
#define HEADER_LEN (sizeof(wire_len_t))
/* We carefully never allow sizes > 64M, so this is an impossible value. */
#define INSIDE_HEADER_BIT WIRE_LEN_LIMIT
/* arg->u2.s contains length we've read, arg->u1.vp contains u8 **data. */
static int do_read_wire_header(int fd, struct io_plan_arg *arg)
{
ssize_t ret;
size_t len = arg->u2.s & ~INSIDE_HEADER_BIT;
u8 *p = *(u8 **)arg->u1.vp;
ret = read(fd, p + len, HEADER_LEN - len);
if (ret <= 0)
return -1;
arg->u2.s += ret;
/* Length bytes read? Set up for normal read of data. */
if (arg->u2.s == INSIDE_HEADER_BIT + HEADER_LEN) {
arg->u2.s = wirelen_to_cpu(*(wire_len_t *)p);
if (arg->u2.s >= INSIDE_HEADER_BIT) {
errno = E2BIG;
return -1;
}
/* A type-only message is not unheard of, so optimize a little */
if (arg->u2.s != HEADER_LEN)
tal_resize((u8 **)arg->u1.vp, arg->u2.s);
arg->u1.vp = *(u8 **)arg->u1.vp;
}
return arg->u2.s == 0;
}
static int do_read_wire(int fd, struct io_plan_arg *arg)
{
ssize_t ret;
/* Still reading header? */
if (arg->u2.s & INSIDE_HEADER_BIT)
return do_read_wire_header(fd, arg);
/* Normal read */
ret = read(fd, arg->u1.cp, arg->u2.s);
if (ret <= 0)
return -1;
arg->u1.cp += ret;
arg->u2.s -= ret;
return arg->u2.s == 0;
}
struct io_plan *io_read_wire_(struct io_conn *conn,
const tal_t *ctx,
u8 **data,
struct io_plan *(*next)(struct io_conn *, void *),
void *next_arg)
{
struct io_plan_arg *arg = io_plan_arg(conn, IO_IN);
/* We allocate data now; saves storing ctx, and lets us read in len. */
arg->u1.vp = data;
*data = tal_arr(ctx, u8, HEADER_LEN);
/* We use u2 to store the length we've read. */
arg->u2.s = INSIDE_HEADER_BIT;
return io_set_plan(conn, IO_IN, do_read_wire, next, next_arg);
}
/* arg->u2.s contains length we've written, arg->u1 contains u8 *data. */
static int do_write_wire_header(int fd, struct io_plan_arg *arg)
{
ssize_t ret;
size_t len = arg->u2.s & ~INSIDE_HEADER_BIT;
wire_len_t hdr = cpu_to_wirelen(tal_count(arg->u1.const_vp));
ret = write(fd, (char *)&hdr + len, HEADER_LEN - len);
if (ret <= 0)
return -1;
arg->u2.s += ret;
/* Both bytes written? Set up for normal write of data. */
if (arg->u2.s == INSIDE_HEADER_BIT + HEADER_LEN)
arg->u2.s = 0;
return 0;
}
static int do_write_wire(int fd, struct io_plan_arg *arg)
{
ssize_t ret;
size_t totlen = tal_bytelen(arg->u1.cp);
/* Still writing header? */
if (arg->u2.s & INSIDE_HEADER_BIT)
return do_write_wire_header(fd, arg);
/* Normal write */
ret = write(fd, arg->u1.cp + arg->u2.s, totlen - arg->u2.s);
if (ret < 0)
return -1;
arg->u2.s += ret;
if (arg->u2.s != totlen)
return 0;
io_write_wire: always make a copy (or steal if take). I caught the gossip daemon freeing a message, while it was queued to be written. Using tal_dup_arr() is the Right Thing, as it handles taken() properly automatically. ------------------------------- Valgrind errors -------------------------------- Valgrind error file: /tmp/lightning-rvc7d5oi/test_forward/lightning-3/valgrind-errors ==11057== Invalid read of size 8 ==11057== at 0x1328F2: to_tal_hdr (tal.c:174) ==11057== by 0x133894: tal_len (tal.c:659) ==11057== by 0x11BBE7: do_write_wire (wire_io.c:103) ==11057== by 0x127B95: do_plan (io.c:369) ==11057== by 0x127C31: io_ready (io.c:390) ==11057== by 0x129461: io_loop (poll.c:295) ==11057== by 0x10CBB4: main (gossip.c:722) ==11057== Address 0x55a99d8 is 24 bytes inside a block of size 200 free'd ==11057== at 0x4C2ED5B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==11057== by 0x133000: del_tree (tal.c:416) ==11057== by 0x132F77: del_tree (tal.c:405) ==11057== by 0x13333E: tal_free (tal.c:504) ==11057== by 0x1123F1: queue_broadcast (broadcast.c:38) ==11057== by 0x111EB0: handle_node_announcement (routing.c:918) ==11057== by 0x10B166: handle_gossip_msg (gossip.c:170) ==11057== by 0x10B76B: owner_msg_in (gossip.c:335) ==11057== by 0x12712E: next_plan (io.c:59) ==11057== by 0x127BD0: do_plan (io.c:376) ==11057== by 0x127C09: io_ready (io.c:386) ==11057== by 0x129461: io_loop (poll.c:295) ==11057== Block was alloc'd at ==11057== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==11057== by 0x132AE7: allocate (tal.c:245) ==11057== by 0x1330A3: tal_alloc_ (tal.c:443) ==11057== by 0x1332A6: tal_alloc_arr_ (tal.c:491) ==11057== by 0x133FEC: tal_dup_ (tal.c:846) ==11057== by 0x112347: new_queued_message (broadcast.c:20) ==11057== by 0x11240B: queue_broadcast (broadcast.c:43) ==11057== by 0x111EB0: handle_node_announcement (routing.c:918) ==11057== by 0x10B166: handle_gossip_msg (gossip.c:170) ==11057== by 0x10B76B: owner_msg_in (gossip.c:335) ==11057== by 0x12712E: next_plan (io.c:59) ==11057== by 0x127BD0: do_plan (io.c:376) Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> wire_io: make a copy in io_write_wire (unless taken()). I hit a corner case where gossipd freed a duplicate while it was being sent out; this kind of thing doesn't happen if io_write_wire() makes a copy by default. We also do a memcheck() here; this gives us a caller in the backtrace if there are uninitialized bytes, rather than waiting until the write which happens later. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2017-05-19 12:03:28 +01:00
tal_free(arg->u1.cp);
return 1;
}
/* Write message from data (tal_count(data) gives length). */
struct io_plan *io_write_wire_(struct io_conn *conn,
const u8 *data,
struct io_plan *(*next)(struct io_conn *, void *),
void *next_arg)
{
struct io_plan_arg *arg = io_plan_arg(conn, IO_OUT);
if (tal_bytelen(data) >= INSIDE_HEADER_BIT) {
errno = E2BIG;
return io_close(conn);
}
arg->u1.const_vp = tal_dup_talarr(conn, u8,
memcheck(data, tal_bytelen(data)));
/* We use u2 to store the length we've written. */
arg->u2.s = INSIDE_HEADER_BIT;
return io_set_plan(conn, IO_OUT, do_write_wire, next, next_arg);
}