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