source: rtems/cpukit/libcsupport/src/libio.c @ c2287ba2

5
Last change on this file since c2287ba2 was c2287ba2, checked in by Sebastian Huber <sebastian.huber@…>, on 03/10/20 at 16:07:19

libio: Robust file descriptor reference counting

There was a race conditon in the reference counting of file descriptors
during a close() operation. After the call to the close handler, the
rtems_libio_free() function cleared the flags to zero. However, at this
point in time there may still exist some holders of the file descriptor.
With RTEMS_DEBUG enabled this could lead to failed assertions in
rtems_libio_iop_drop().

Change the code to use only atomic read-modify-write operations on the
rtems_libio_iop::flags.

  • Property mode set to 100644
File size: 3.6 KB
Line 
1/**
2 *  @file
3 *
4 *  @brief File Descriptor Routines
5 *  @ingroup LibIOInternal
6 */
7
8/*
9 *  COPYRIGHT (c) 1989-1999.
10 *  On-Line Applications Research Corporation (OAR).
11 *
12 *  The license and distribution terms for this file may be
13 *  found in the file LICENSE in this distribution or at
14 *  http://www.rtems.org/license/LICENSE.
15 */
16
17#if HAVE_CONFIG_H
18#include "config.h"
19#endif
20
21#include <fcntl.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include <rtems.h>
28#include <rtems/libio_.h>
29#include <rtems/assoc.h>
30
31/* define this to alias O_NDELAY to  O_NONBLOCK, i.e.,
32 * O_NDELAY is accepted on input but fcntl(F_GETFL) returns
33 * O_NONBLOCK. This is because rtems has no distinction
34 * between the two (but some systems have).
35 * Note that accepting this alias creates a problem:
36 * an application trying to clear the non-blocking flag
37 * using a
38 *
39 *    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NDELAY);
40 *
41 * does (silently) ignore the operation.
42 */
43#undef ACCEPT_O_NDELAY_ALIAS
44
45static const rtems_assoc_t access_modes_assoc[] = {
46  { "READ",       LIBIO_FLAGS_READ,  O_RDONLY },
47  { "WRITE",      LIBIO_FLAGS_WRITE, O_WRONLY },
48  { "READ/WRITE", LIBIO_FLAGS_READ_WRITE, O_RDWR },
49  { 0, 0, 0 },
50};
51
52static const rtems_assoc_t status_flags_assoc[] = {
53#ifdef ACCEPT_O_NDELAY_ALIAS
54  { "NO DELAY",  LIBIO_FLAGS_NO_DELAY,  O_NDELAY },
55#endif
56  { "NONBLOCK",  LIBIO_FLAGS_NO_DELAY,  O_NONBLOCK },
57  { "APPEND",    LIBIO_FLAGS_APPEND,    O_APPEND },
58  { 0, 0, 0 },
59};
60
61unsigned int rtems_libio_fcntl_flags( int fcntl_flags )
62{
63  unsigned int   flags = 0;
64  uint32_t   access_modes;
65
66  /*
67   * Access mode is a small integer
68   */
69
70  access_modes = (unsigned int) (fcntl_flags & O_ACCMODE);
71  fcntl_flags &= ~O_ACCMODE;
72  flags = rtems_assoc_local_by_remote( access_modes_assoc, access_modes );
73
74  /*
75   * Everything else is single bits
76   */
77
78  flags |= (unsigned int ) rtems_assoc_local_by_remote_bitfield(
79    status_flags_assoc,
80    (uint32_t) fcntl_flags
81  );
82
83  return flags;
84}
85
86int rtems_libio_to_fcntl_flags( unsigned int flags )
87{
88  int fcntl_flags = 0;
89
90  if ( (flags & LIBIO_FLAGS_READ_WRITE) == LIBIO_FLAGS_READ_WRITE ) {
91    fcntl_flags |= O_RDWR;
92  } else if ( (flags & LIBIO_FLAGS_READ) == LIBIO_FLAGS_READ) {
93    fcntl_flags |= O_RDONLY;
94  } else if ( (flags & LIBIO_FLAGS_WRITE) == LIBIO_FLAGS_WRITE) {
95    fcntl_flags |= O_WRONLY;
96  }
97
98  if ( (flags & LIBIO_FLAGS_NO_DELAY) == LIBIO_FLAGS_NO_DELAY ) {
99    fcntl_flags |= O_NONBLOCK;
100  }
101
102  if ( (flags & LIBIO_FLAGS_APPEND) == LIBIO_FLAGS_APPEND ) {
103    fcntl_flags |= O_APPEND;
104  }
105
106  return fcntl_flags;
107}
108
109rtems_libio_t *rtems_libio_allocate( void )
110{
111  rtems_libio_t *iop;
112
113  rtems_libio_lock();
114
115  iop = rtems_libio_iop_free_head;
116
117  if ( iop != NULL ) {
118    void *next;
119
120    next = iop->data1;
121    rtems_libio_iop_free_head = next;
122
123    if ( next == NULL ) {
124      rtems_libio_iop_free_tail = &rtems_libio_iop_free_head;
125    }
126  }
127
128  rtems_libio_unlock();
129
130  return iop;
131}
132
133void rtems_libio_free(
134  rtems_libio_t *iop
135)
136{
137  size_t zero;
138
139  rtems_filesystem_location_free( &iop->pathinfo );
140
141  rtems_libio_lock();
142
143  /*
144   * Clear everything except the reference count part.  At this point in time
145   * there may be still some holders of this file descriptor.
146   */
147  rtems_libio_iop_flags_clear( iop, LIBIO_FLAGS_REFERENCE_INC - 1U );
148  zero = offsetof( rtems_libio_t, offset );
149  memset( (char *) iop + zero, 0, sizeof( *iop ) - zero );
150
151  /*
152   * Append it to the free list.  This increases the likelihood that a use
153   * after close is detected.
154   */
155  *rtems_libio_iop_free_tail = iop;
156  rtems_libio_iop_free_tail = &iop->data1;
157
158  rtems_libio_unlock();
159}
Note: See TracBrowser for help on using the repository browser.