* ==================== CORE CHANGES ===================
+* --track-fds=yes will now also warn about double closing of file
+ descriptors. Printing the context where the file descriptor was
+ originally opened and where it was previously closed.
+
* ================== PLATFORM CHANGES =================
* ==================== TOOL CHANGES ===================
466762 Add redirs for C23 free_sized() and free_aligned_sized()
466884 Missing writev uninit padding suppression for _XSend
471036 disInstr_AMD64: disInstr miscalculated next %rip on RORX imm8, m32/64, r32/6
+471222 support tracking of file descriptors being double closed
475498 Add reallocarray wrapper
476320 Build failure with GCC
476331 clean up generated/distributed filter scripts
Bool ML_(fd_allowed)(Int fd, const HChar *syscallname, ThreadId tid,
Bool isNewFD);
-extern void ML_(record_fd_close) (Int fd);
+extern void ML_(record_fd_close) (ThreadId tid, Int fd);
+extern void ML_(record_fd_close_range) (ThreadId tid, Int fd);
extern void ML_(record_fd_open_named) (ThreadId tid, Int fd);
extern void ML_(record_fd_open_nameless) (ThreadId tid, Int fd);
extern void ML_(record_fd_open_with_given_name)(ThreadId tid, Int fd,
GENXY(__NR_read, sys_read), // 0
GENX_(__NR_write, sys_write), // 1
GENXY(__NR_open, sys_open), // 2
- GENXY(__NR_close, sys_close), // 3
+ GENX_(__NR_close, sys_close), // 3
GENXY(__NR_stat, sys_newstat), // 4
GENXY(__NR_fstat, sys_newfstat), // 5
GENX_(__NR_write, sys_write), // 4
GENXY(__NR_open, sys_open), // 5
- GENXY(__NR_close, sys_close), // 6
+ GENX_(__NR_close, sys_close), // 6
// GENXY(__NR_waitpid, sys_waitpid), // 7
GENXY(__NR_creat, sys_creat), // 8
GENX_(__NR_link, sys_link), // 9
LINX_(__NR_fchownat, sys_fchownat), // 54
GENX_(__NR_fchown, sys_fchown), // 55
LINXY(__NR_openat, sys_openat), // 56
- GENXY(__NR_close, sys_close), // 57
+ GENX_(__NR_close, sys_close), // 57
LINX_(__NR_vhangup, sys_vhangup), // 58
LINXY(__NR_pipe2, sys_pipe2), // 59
LINX_(__NR_quotactl, sys_quotactl), // 60
GENXY(__NR_read, sys_read),
GENX_(__NR_write, sys_write),
GENXY(__NR_open, sys_open),
- GENXY(__NR_close, sys_close),
+ GENX_(__NR_close, sys_close),
GENXY(__NR_wait4, sys_wait4),
_____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(8)), // old creat
GENX_(__NR_link, sys_link),
GENXY(__NR_read_nocancel, sys_read),
GENX_(__NR_write_nocancel, sys_write),
GENXY(__NR_open_nocancel, sys_open),
- GENXY(__NR_close_nocancel, sys_close),
+ GENX_(__NR_close_nocancel, sys_close),
GENXY(__NR_wait4_nocancel, sys_wait4), // 400
MACXY(__NR_recvmsg_nocancel, recvmsg),
MACX_(__NR_sendmsg_nocancel, sendmsg),
if ((fd != 2/*stderr*/ || VG_(debugLog_getLevel)() == 0)
&& fd != VG_(log_output_sink).fd
&& fd != VG_(xml_output_sink).fd)
- ML_(record_fd_close)(fd);
+ ML_(record_fd_close)(tid, fd);
}
#endif
GENX_(__NR_write, sys_write), // 4
GENXY(__NR_open, sys_open), // 5
- GENXY(__NR_close, sys_close), // 6
+ GENX_(__NR_close, sys_close), // 6
GENXY(__NR_wait4, sys_wait4), // 7
// 4.3 creat 8
#include "config.h"
+static
+HChar *getsockdetails(Int fd, UInt len, HChar *buf);
+
void ML_(guess_and_register_stack) (Addr sp, ThreadState* tst)
{
Bool debug = False;
Int fd; /* The file descriptor */
HChar *pathname; /* NULL if not a regular file or unknown */
ExeContext *where; /* NULL if inherited from parent */
+ ExeContext *where_closed; /* record the last close of fd */
+ Bool fd_closed;
struct OpenFd *next, *prev;
} OpenFd;
/* Count of open file descriptors. */
static Int fd_count = 0;
+/* Close_range caller might want to close very wide range of file descriptors,
+ up to 0U. We want to avoid iterating through such a range in a normall
+ close_range, just up to any open file descriptor. Also, unlike
+ record_fd_close_range, we assume the user might deliberately double closes
+ any file descriptors in the range, so don't warn about double close here. */
+void ML_(record_fd_close_range)(ThreadId tid, Int from_fd)
+{
+ OpenFd *i = allocated_fds;
+
+ if (from_fd >= VG_(fd_hard_limit))
+ return; /* Valgrind internal */
+
+ while(i) {
+ // Assume the user doesn't want a warning if this came from
+ // close_range. Just record the file descriptors not yet closed here.
+ if (i->fd >= from_fd && !i->fd_closed
+ && i->fd != VG_(log_output_sink).fd
+ && i->fd != VG_(xml_output_sink).fd) {
+ i->fd_closed = True;
+ i->where_closed = ((tid == -1)
+ ? NULL
+ : VG_(record_ExeContext)(tid,
+ 0/*first_ip_delta*/));
+ fd_count--;
+ }
+ i = i->next;
+ }
+}
+
/* Note the fact that a file descriptor was just closed. */
-void ML_(record_fd_close)(Int fd)
+void ML_(record_fd_close)(ThreadId tid, Int fd)
{
OpenFd *i = allocated_fds;
return; /* Valgrind internal */
while(i) {
- if(i->fd == fd) {
- if(i->prev)
- i->prev->next = i->next;
- else
- allocated_fds = i->next;
- if(i->next)
- i->next->prev = i->prev;
- if(i->pathname)
- VG_(free) (i->pathname);
- VG_(free) (i);
- fd_count--;
- break;
+ if (i->fd == fd) {
+ if (i->fd_closed) {
+ char *path = i->pathname;
+ // Note we might want to turn this into a "Core error"
+ // Or use pub_core_execontext later.
+ // VG_(print_ExeContext_stats) (True ...);
+ VG_(emit)("File descriptor %d: %s is already closed\n",
+ fd, path);
+ VG_(pp_ExeContext)(VG_(record_ExeContext)(tid, 0));
+ VG_(emit)(" Previously closed\n");
+ VG_(pp_ExeContext)(i->where_closed);
+ VG_(emit)(" Originally opened\n");
+ VG_(pp_ExeContext)(i->where);
+ } else {
+ i->fd_closed = True;
+ i->where_closed = ((tid == -1)
+ ? NULL
+ : VG_(record_ExeContext)(tid,
+ 0/*first_ip_delta*/));
+ /* Record path/socket name, etc. In case we want to print it later,
+ for example for double close. Note that record_fd_close is
+ actually called from the PRE syscall handler, so the file
+ description is about to be closed, but hasn't yet at this
+ point. */
+ if (!i->pathname) {
+ Int val;
+ Int len = sizeof(val);
+ if (VG_(getsockopt)(i->fd, VKI_SOL_SOCKET, VKI_SO_TYPE,
+ &val, &len) == -1) {
+ VG_(printf)("not sockopt: %d\n", i->fd);
+ HChar *pathname = VG_(malloc)("vg.record_fd_close.fd", 30);
+ VG_(snprintf)(pathname, 30, "file descriptor %d\n", i->fd);
+ i->pathname = pathname;
+ } else {
+ HChar *name = VG_(malloc)("vg.record_fd_close.sock", 256);
+ i->pathname = getsockdetails(i->fd, 256, name);
+ }
+ }
+ fd_count--;
+ }
+ break;
}
i = i->next;
}
if (fd >= VG_(fd_hard_limit))
return; /* Valgrind internal */
- /* Check to see if this fd is already open. */
+ /* Check to see if this fd is already open (or closed, we will just
+ override it. */
i = allocated_fds;
while (i) {
if (i->fd == fd) {
- if (i->pathname) VG_(free)(i->pathname);
+ if (i->pathname) {
+ VG_(free)(i->pathname);
+ i->pathname = NULL;
+ }
+ if (i->fd_closed) /* now we will open it. */
+ fd_count++;
break;
}
i = i->next;
i->fd = fd;
i->pathname = VG_(strdup)("syswrap.rfdowgn.2", pathname);
i->where = (tid == -1) ? NULL : VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
+ i->fd_closed = False;
+ i->where_closed = NULL;
}
// Record opening of an fd, and find its name.
{
OpenFd *i = allocated_fds;
while (i) {
- if (i->fd == fd)
- return True;
+ if (i->fd == fd) {
+ if (i->fd_closed)
+ return False;
+ else
+ return True;
+ }
i = i->next;
}
return False;
OpenFd *i = allocated_fds;
while (i) {
- if (i->fd == fd)
- return i->pathname;
+ if (i->fd == fd) {
+ if (i->fd_closed)
+ return NULL;
+ else
+ return i->pathname;
+ }
i = i->next;
}
/*
* Try get some details about a socket.
+ * Returns the given BUF with max length LEN.
*/
-static void
-getsockdetails(Int fd)
+static
+HChar *getsockdetails(Int fd, UInt len, HChar *buf)
{
union u {
struct vki_sockaddr a;
Int plen = sizeof(struct vki_sockaddr_in);
if (VG_(getpeername)(fd, (struct vki_sockaddr *)&paddr, &plen) != -1) {
- VG_(message)(Vg_UserMsg, "Open AF_INET socket %d: %s <-> %s\n", fd,
- inet_to_name(&(laddr.in), llen, lname),
- inet_to_name(&paddr, plen, pname));
+ VG_(snprintf)(buf, len, "AF_INET socket %d: %s <-> %s", fd,
+ inet_to_name(&(laddr.in), llen, lname),
+ inet_to_name(&paddr, plen, pname));
+ return buf;
} else {
- VG_(message)(Vg_UserMsg, "Open AF_INET socket %d: %s <-> unbound\n",
- fd, inet_to_name(&(laddr.in), llen, lname));
- }
- return;
+ VG_(snprintf)(buf, len, "AF_INET socket %d: %s <-> <unbound>",
+ fd, inet_to_name(&(laddr.in), llen, lname));
+ return buf;
}
+ }
case VKI_AF_INET6: {
HChar lname[128]; // large enough
HChar pname[128]; // large enough
Int plen = sizeof(struct vki_sockaddr_in6);
if (VG_(getpeername)(fd, (struct vki_sockaddr *)&paddr, &plen) != -1) {
- VG_(message)(Vg_UserMsg, "Open AF_INET6 socket %d: %s <-> %s\n", fd,
- inet6_to_name(&(laddr.in6), llen, lname),
- inet6_to_name(&paddr, plen, pname));
+ VG_(snprintf)(buf, len, "AF_INET6 socket %d: %s <-> %s", fd,
+ inet6_to_name(&(laddr.in6), llen, lname),
+ inet6_to_name(&paddr, plen, pname));
+ return buf;
} else {
- VG_(message)(Vg_UserMsg, "Open AF_INET6 socket %d: %s <-> unbound\n",
+ VG_(snprintf)(buf, len, "AF_INET6 socket %d: %s <-> <unbound>",
fd, inet6_to_name(&(laddr.in6), llen, lname));
+ return buf;
}
- return;
- }
+ }
case VKI_AF_UNIX: {
static char lname[256];
- VG_(message)(Vg_UserMsg, "Open AF_UNIX socket %d: %s\n", fd,
- unix_to_name(&(laddr.un), llen, lname));
- return;
+ VG_(snprintf)(buf, len, "AF_UNIX socket %d: %s", fd,
+ unix_to_name(&(laddr.un), llen, lname));
+ return buf;
}
default:
- VG_(message)(Vg_UserMsg, "Open pf-%d socket %d:\n",
- laddr.a.sa_family, fd);
- return;
+ VG_(snprintf)(buf, len, "pf-%d socket %d",
+ laddr.a.sa_family, fd);
+ return buf;
}
}
- VG_(message)(Vg_UserMsg, "Open socket %d:\n", fd);
+ VG_(snprintf)(buf, len, "socket %d", fd);
+ return buf;
}
int non_std = 0;
for (i = allocated_fds; i; i = i->next) {
- if (i->fd > 2)
+ if (i->fd > 2 && i->fd_closed != True)
non_std++;
}
fd_count, fd_count - non_std, when);
for (i = allocated_fds; i; i = i->next) {
+ if (i->fd_closed)
+ continue;
+
if (i->fd <= 2 && VG_(clo_track_fds) < 2)
continue;
== -1) {
VG_(message)(Vg_UserMsg, "Open file descriptor %d:\n", i->fd);
} else {
- getsockdetails(i->fd);
+ HChar buf[256];
+ VG_(message)(Vg_UserMsg, "Open %s\n",
+ getsockdetails(i->fd, 256, buf));
}
}
/* croak? */
if ((!allowed) && VG_(showing_core_errors)() ) {
- VG_(message)(Vg_UserMsg,
+ VG_(message)(Vg_UserMsg,
"Warning: invalid file descriptor %d in syscall %s()\n",
fd, syscallname);
if (fd == VG_(log_output_sink).fd && VG_(log_output_sink).fd >= 0)
allow that to be closed either. */
|| (ARG1 == 2/*stderr*/ && VG_(debugLog_getLevel)() > 0) )
SET_STATUS_Failure( VKI_EBADF );
-}
-
-POST(sys_close)
-{
- if (VG_(clo_track_fds)) ML_(record_fd_close)(ARG1);
+ else {
+ /* We used to do close tracking in the POST handler, but that is
+ only called on success. Even if the close syscall fails the
+ file descriptor is still really closed/invalid. So we do the
+ recording and checking here. */
+ if (VG_(clo_track_fds)) ML_(record_fd_close)(tid, ARG1);
+ }
}
PRE(sys_dup)
if (last >= VG_(fd_hard_limit))
last = VG_(fd_hard_limit) - 1;
- for (fd = ARG1; fd <= last; fd++)
- if ((fd != 2/*stderr*/ || VG_(debugLog_getLevel)() == 0)
- && fd != VG_(log_output_sink).fd
- && fd != VG_(xml_output_sink).fd)
- ML_(record_fd_close)(fd);
+ /* If the close_range range is too wide, we don't want to loop
+ through the whole range. */
+ if (ARG2 == ~0U)
+ ML_(record_fd_close_range)(tid, ARG1);
+ else {
+ for (fd = ARG1; fd <= last; fd++)
+ if ((fd != 2/*stderr*/ || VG_(debugLog_getLevel)() == 0)
+ && fd != VG_(log_output_sink).fd
+ && fd != VG_(xml_output_sink).fd)
+ ML_(record_fd_close)(tid, fd);
+ }
}
GENXY (__NR_read, sys_read), // 3
GENX_ (__NR_write, sys_write), // 4
GENXY (__NR_open, sys_open), // 5
- GENXY (__NR_close, sys_close), // 6
+ GENX_ (__NR_close, sys_close), // 6
GENXY (__NR_waitpid, sys_waitpid), // 7
GENXY (__NR_creat, sys_creat), // 8
GENX_ (__NR_link, sys_link), // 9
GENXY (__NR_read, sys_read), /* 5000 */
GENX_ (__NR_write, sys_write),
GENXY (__NR_open, sys_open),
- GENXY (__NR_close, sys_close),
+ GENX_ (__NR_close, sys_close),
GENXY (__NR_stat, sys_newstat),
GENXY (__NR_fstat, sys_newfstat),
GENXY (__NR_lstat, sys_newlstat),
LINX_ (__NR_fchownat, sys_fchownat),
GENX_ (__NR_fchown, sys_fchown),
LINXY (__NR_openat, sys_openat),
- GENXY (__NR_close, sys_close),
+ GENX_ (__NR_close, sys_close),
LINX_ (__NR_vhangup, sys_vhangup),
LINXY (__NR_pipe2, sys_pipe2),
LINX_ (__NR_quotactl, sys_quotactl),
GENX_(__NR_write, sys_write), // 4
GENXY(__NR_open, sys_open), // 5
- GENXY(__NR_close, sys_close), // 6
+ GENX_(__NR_close, sys_close), // 6
GENXY(__NR_waitpid, sys_waitpid), // 7
GENXY(__NR_creat, sys_creat), // 8
GENX_(__NR_link, sys_link), // 9
GENX_(__NR_write, sys_write), // 4
GENXY(__NR_open, sys_open), // 5
- GENXY(__NR_close, sys_close), // 6
+ GENX_(__NR_close, sys_close), // 6
GENXY(__NR_waitpid, sys_waitpid), // 7
GENXY(__NR_creat, sys_creat), // 8
GENX_(__NR_link, sys_link), // 9
GENX_(__NR_write, sys_write), // 4
GENXY(__NR_open, sys_open), // 5
- GENXY(__NR_close, sys_close), // 6
+ GENX_(__NR_close, sys_close), // 6
// ?????(__NR_restart_syscall, ), // 7
GENXY(__NR_creat, sys_creat), // 8
GENX_(__NR_link, sys_link), // 9
/* Possibly an explicitly open'ed client door fd was just closed.
Generic sys_close wrapper calls this only if VG_(clo_track_fds) = True. */
if (!VG_(clo_track_fds))
- ML_(record_fd_close)(ARG1);
+ ML_(record_fd_close)(tid, ARG1);
}
PRE(sys_linkat)
if ((desc->d_attributes & DOOR_DESCRIPTOR) &&
(desc->d_attributes & DOOR_RELEASE)) {
Int fd = desc->d_data.d_desc.d_descriptor;
- ML_(record_fd_close)(fd);
+ ML_(record_fd_close)(tid, fd);
}
}
}
case VKI_DOOR_REVOKE:
door_record_revoke(tid, ARG1);
if (VG_(clo_track_fds))
- ML_(record_fd_close)(ARG1);
+ ML_(record_fd_close)(tid, ARG1);
break;
case VKI_DOOR_INFO:
POST_MEM_WRITE(ARG2, sizeof(vki_door_info_t));
GENX_(__NR_write, sys_write), // 4
GENXY(__NR_open, sys_open), // 5
- GENXY(__NR_close, sys_close), // 6
+ GENX_(__NR_close, sys_close), // 6
GENXY(__NR_waitpid, sys_waitpid), // 7
GENXY(__NR_creat, sys_creat), // 8
GENX_(__NR_link, sys_link), // 9
vgprintf.stderr.exp vgprintf.vgtest \
vgprintf_nvalgrind.stderr.exp vgprintf_nvalgrind.vgtest \
process_vm_readv_writev.stderr.exp process_vm_readv_writev.vgtest \
- sigprocmask.stderr.exp sigprocmask.vgtest
+ sigprocmask.stderr.exp sigprocmask.vgtest \
+ socket_close.stderr.exp socket_close.vgtest \
+ file_dclose.stderr.exp file_dclose.vgtest \
+ double_close_range.stderr.exp double_close_range.vgtest
check_PROGRAMS = \
coolo_sigaction \
gxx304 \
process_vm_readv_writev \
- sigprocmask
+ sigprocmask \
+ socket_close \
+ file_dclose
+
+if HAVE_CLOSE_RANGE
+ check_PROGRAMS += double_close_range
+endif
if HAVE_NESTED_FUNCTIONS
check_PROGRAMS += nestedfns
--- /dev/null
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(__linux__)
+#include <linux/close_range.h>
+#endif
+
+int main ()
+{
+ int fd = dup (2);
+ if (fd != -1){
+ fprintf(stderr, "close fd %d\n", fd);
+ close (fd);
+ }
+
+ // Shouldn't warn, we are closing everything
+ fprintf(stderr, "Closing range (%d, %d).\n", fd, ~0U);
+ close_range(6, ~0U, 0);
+
+ // Should warn, we close a small range (but only for fd itself
+ fprintf(stderr, "Closing range (%d, %d).\n", fd, 7);
+ close_range(5, 7, 0);
+
+ return 0;
+}
--- /dev/null
+close fd 3
+Closing range (3, -1).
+Closing range (3, 7).
--- /dev/null
+prog: double_close_range
+prereq: test -x double_close_range
+vgopts: -q --track-fds=yes
#ifndef _FDLEAK_H_
#define _FDLEAK_H_
+#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
+#include <sys/stat.h>
#define DO(op) \
({ \
* https://bugs.launchpad.net/ubuntu/+source/seahorse/+bug/235184
*/
static inline void close_inherited () {
+ struct stat sb;
int i; int max_fds = sysconf (_SC_OPEN_MAX);
if (max_fds < 0)
max_fds = 1024; /* Fallback if sysconf fails, returns -1. */
/* Only leave 0 (stdin), 1 (stdout) and 2 (stderr) open. */
for (i = 3; i < max_fds; i++)
- close(i);
+ if (fstat (i, &sb) != -1) /* Test if the file descriptor exists first. */
+ close(i);
}
#define CLOSE_INHERITED_FDS close_inherited ()
+/* Note that the following would be nicer, but close_range is fairly new. */
+// #define CLOSE_INHERITED_FDS close_range (3, ~0U, 0)
#endif /* _FDLEAK_H_ */
exit(1);
}
+ // Extra double close test...
+ // Just to see that the description is OK.
+ int fd2 = DO( dup (s) );
+ close (fd2);
+ close (fd2); // oops.
+
(void) DO( read(s, buf, sizeof(buf)) );
printf("%s\n", buf);
+
}
+File descriptor 4: AF_INET socket 4: 127.0.0.1:... <-> 127.0.0.1:... is already closed
+ at 0x........: close (in /...libc...)
+ by 0x........: client (fdleak_ipv4.c:70)
+ by 0x........: main (fdleak_ipv4.c:90)
+ Previously closed
+ at 0x........: close (in /...libc...)
+ by 0x........: client (fdleak_ipv4.c:69)
+ by 0x........: main (fdleak_ipv4.c:90)
+ Originally opened
+ at 0x........: dup (in /...libc...)
+ by 0x........: client (fdleak_ipv4.c:68)
+ by 0x........: main (fdleak_ipv4.c:90)
FILE DESCRIPTORS: 5 open (3 std) at exit.
Open AF_INET socket 4: 127.0.0.1:... <-> 127.0.0.1:...
...
-Open AF_INET socket 3: 127.0.0.1:... <-> unbound
+Open AF_INET socket 3: 127.0.0.1:... <-> <unbound>
...
--- /dev/null
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+static int
+openfile (const char *f)
+{
+ return creat (f, O_RDWR);
+}
+
+static void
+closefile (const char *f, int fd)
+{
+ close (fd);
+ unlink (f);
+}
+
+int main ()
+{
+ const char *TMPFILE = "file_dclose.tmp";
+ int fd;
+
+ fd = openfile (TMPFILE);
+ if (fd != -1) {
+ fprintf(stderr, "close %d\n", fd);
+ close (fd);
+ }
+
+ fprintf (stderr, "time passes and we close %d again\n", fd);
+ closefile (TMPFILE, fd);
+
+ return 0;
+}
--- /dev/null
+close 3
+time passes and we close 3 again
+File descriptor 3: file_dclose.tmp is already closed
+ at 0x........: close (in /...libc...)
+ by 0x........: closefile (file_dclose.c:16)
+ by 0x........: main (file_dclose.c:32)
+ Previously closed
+ at 0x........: close (in /...libc...)
+ by 0x........: main (file_dclose.c:28)
+ Originally opened
+ at 0x........: creat (in /...libc...)
+ by 0x........: openfile (file_dclose.c:10)
+ by 0x........: main (file_dclose.c:25)
--- /dev/null
+prog: file_dclose
+prereq: test -x file_dclose
+vgopts: -q --track-fds=yes
--- /dev/null
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/un.h>
+
+
+int socket_fd;
+
+void *open_socket()
+{
+ socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ fprintf (stderr, "Open socket %d\n", socket_fd);
+ struct sockaddr_un my_addr;
+
+ memset(&my_addr, 0, sizeof(my_addr));
+ my_addr.sun_family = AF_UNIX;
+ strncpy(my_addr.sun_path, "/tmp/foofrob", sizeof(my_addr.sun_path) - 1);
+ bind(socket_fd, (struct sockaddr *) &my_addr, sizeof(my_addr));
+
+ return NULL;
+}
+
+int main ()
+{
+ open_socket();
+
+ if (socket_fd != -1)
+ {
+ fprintf(stderr, "close socket_fd %d\n", socket_fd);
+ close (socket_fd);
+ }
+
+ fprintf (stderr, "and close the socket again %d\n", socket_fd);
+ close (socket_fd);
+
+ return 0;
+}
+
--- /dev/null
+Open socket 3
+close socket_fd 3
+and close the socket again 3
+File descriptor 3: AF_UNIX socket 3: <unknown> is already closed
+ at 0x........: close (in /...libc...)
+ by 0x........: main (socket_close.c:37)
+ Previously closed
+ at 0x........: close (in /...libc...)
+ by 0x........: main (socket_close.c:33)
+ Originally opened
+ at 0x........: socket (in /...libc...)
+ by 0x........: open_socket (socket_close.c:14)
+ by 0x........: main (socket_close.c:28)
--- /dev/null
+prog: socket_close
+prereq: test -x socket_close
+vgopts: -q --track-fds=yes