Bug#1142663: libsocket-msghdr-perl: recvmsg fails to set flags (.msg_flags)
Eric Wong
e at 80x24.org
Thu Jul 23 22:44:59 BST 2026
Package: libsocket-msghdr-perl
Version: 0.05-2+b4
Severity: normal
Tags: patch upstream
Dear Maintainer,
recvmsg in Socket::MsgHdr fails to set the `flags' value to
what the recvmsg(2) syscall writes to `struct msghdr'.msg_flags.
This makes it impossible to detect truncation errors such as
MSG_TRUNC and MSG_CTRUNC when the `buflen' and `controllen'
fields are too small. Attached is a patch which fixes the
issue in upstream.
Unfortunately, I cannot contribute directly to upstream due to
Terms of Service and JS requirement of Github causing
accessibility problems.
-------------- next part --------------
>From af9d3624155ab51cd752f27981ef3b7287d24153 Mon Sep 17 00:00:00 2001
From: Eric Wong <e at 80x24.org>
Date: Thu, 23 Jul 2026 20:54:15 +0000
Subject: [PATCH] recvmsg: set `flags' field according to .msg_flags
The .msg_flags field of `struct msghdr' gets populated by the
recvmsg(2) syscall. Most notably, .msg_flags allows the
receiver to detect truncation via MSG_TRUNC and MSG_CTRUNC flags
when the supplied `buflen' and/or `controllen' fields are too
small.
Note that MSG_TRUNC set in .msg_flags by recvfrom is different
from the `flags' argument. MSG_TRUNC in the `flags' argument
changes the return value of recvfrom(2) to the intended length
of the data, whereas .msg_flags merely tells the caller the
buffer is too small and does not tell the intended length.
MSG_TRUNC in the `flags' argument probably makes the most sense
with MSG_PEEK, as shown in the below example to dynamically
size `buflen' to the necessary value to avoid MSG_TRUNC in
.msg_flags in a subsequent recvmsg call:
use Socket;
use Socket::MsgHdr;
my ($rd, $wr);
socketpair($rd, $wr, AF_UNIX, SOCK_DGRAM, 0) or die "socketpair: $!";
my $hdr = Socket::MsgHdr->new(buf => 'ab');
sendmsg($wr, $hdr, 0) or die "sendmsg: $!";
my $m = Socket::MsgHdr->new(buflen => 1);
my $r = recvmsg($rd, $m, MSG_TRUNC|MSG_PEEK) or die "recvmsg: $!";
print "MSG_TRUNC=", MSG_TRUNC, "\n";
print "before: r=$r flags=", $m->flags,
" (expecting (flags & MSG_TRUNC=${\MSG_TRUNC}))\n";
$m = Socket::MsgHdr->new(buflen => $r);
recvmsg($rd, $m, 0) or die "recvmsg: $!";
print " after: r=$r flags=", $m->flags, " (expecting flags==0)\n";
---
MsgHdr.xs | 4 ++++
t/30recv.t | 40 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/MsgHdr.xs b/MsgHdr.xs
index 0e4b932..9b7123a 100644
--- a/MsgHdr.xs
+++ b/MsgHdr.xs
@@ -158,6 +158,10 @@ smh_recvmsg(s, msg_hdr, flags = 0)
SvCUR_set(*svp, RETVAL);
if ((svp = hv_fetch(hsh, "control", 7, FALSE)))
SvCUR_set(*svp, mh.m.msg_controllen);
+ if ((svp = hv_fetch(hsh, "flags", 5, FALSE))) {
+ SvUPGRADE(*svp, SVt_IV);
+ SvIV_set(*svp, mh.m.msg_flags);
+ }
}
OUTPUT:
RETVAL
diff --git a/t/30recv.t b/t/30recv.t
index 7bb2b8e..c12daa4 100644
--- a/t/30recv.t
+++ b/t/30recv.t
@@ -6,7 +6,7 @@
# change 'tests => 1' to 'tests => last_test_to_print';
use Test::More;
-BEGIN { plan tests => 9 };
+BEGIN { plan tests => 11 };
use bytes;
use strict;
use Socket;
@@ -124,4 +124,42 @@ SKIP: {
cmp_ok($dev1, '==', $dev2, "scm_rights fds on same device");
cmp_ok($ino1, '==', $ino2, "scm_rights fds are same inode");
+
+ eval { &MSG_CTRUNC; }; # autoloaded, may not be defined yet
+ skip "msg_ctrunc not defined", 1 if $@;
+
+ socketpair(Rd, Wr, AF_UNIX, SOCK_DGRAM, 0)
+ or die "socketpair: $!\n";
+
+ $hdr = new Socket::MsgHdr(buf => "hello!");
+ $hdr->cmsghdr(SOL_SOCKET, SCM_RIGHTS, pack('i', fileno STDIN));
+ sendmsg(\*Wr, $hdr, 0)
+ or die "sendmsg: $!\n";
+
+ $m = new Socket::MsgHdr(buflen => 256, controllen => 1);
+ recvmsg(\*Rd, $m, 0)
+ or die "recvmsg: $!\n";
+
+ ok($m->flags & MSG_CTRUNC, 'MSG_CTRUNC set when controllen is too short');
+
+ close Rd; close Wr;
+};
+
+# 11
+SKIP: {
+ local (*Rd, *Wr);
+ socketpair(Rd, Wr, AF_UNIX, SOCK_DGRAM, 0)
+ or die "socketpair: $!\n";
+
+ my $hdr = new Socket::MsgHdr(buf => 'ab');
+ sendmsg(\*Wr, $hdr, 0)
+ or die "sendmsg: $!\n";
+
+ my $m = new Socket::MsgHdr(buflen => 1);
+ recvmsg(\*Rd, $m, 0)
+ or die "recvmsg: $!\n";
+
+ ok($m->flags & MSG_TRUNC, 'MSG_TRUNC set when buflen is too short');
+
+ close Rd; close Wr;
};
More information about the pkg-perl-maintainers
mailing list