source: rtems/cpukit/posix/src/mmap.c @ b965f461

5
Last change on this file since b965f461 was b965f461, checked in by Sebastian Huber <sebastian.huber@…>, on 07/20/17 at 05:25:55

posix: Use unprotected chain operations

Operarations are already protected by mmap_mappings_lock.

Updates #2859.

  • Property mode set to 100644
File size: 10.3 KB
Line 
1/**
2 * @file
3 */
4
5/*
6 * Copyright (c) 2012 Chris Johns (chrisj@rtems.org)
7 * Copyright (c) 2017 Gedare Bloom (gedare@rtems.org)
8 * Copyright (c) 2017 Kevin kirspel (kirspkt@gmail.com)
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#if HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#include <rtems.h>
20#include <errno.h>
21#include <stdlib.h>
22#include <sys/mman.h>
23#include <sys/stat.h>
24#include <unistd.h>
25
26#include "rtems/libio_.h"
27
28#include <rtems/posix/mmanimpl.h>
29#include <rtems/posix/shmimpl.h>
30
31#define RTEMS_MUTEX_ATTRIBS \
32  (RTEMS_PRIORITY | RTEMS_SIMPLE_BINARY_SEMAPHORE | \
33   RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL)
34
35/**
36 * Mmap chain of mappings.
37 */
38rtems_chain_control mmap_mappings;
39
40/**
41 * The id of the MMAP lock.
42 */
43static rtems_id mmap_mappings_lock;
44
45/**
46 * Create the lock.
47 */
48static
49bool mmap_mappings_lock_create(
50  void
51)
52{
53  /*
54   * Lock the mapping table. We only create a lock if a call is made. First we
55   * test if a mapping lock is present. If one is present we lock it. If not
56   * the libio lock is locked and we then test the mapping lock again. If not
57   * present we create the mapping lock then release libio lock.
58   */
59  /* FIXME: double-checked locking anti-pattern. */
60  if ( mmap_mappings_lock == 0 ) {
61    rtems_status_code sc = RTEMS_SUCCESSFUL;
62    rtems_chain_initialize_empty( &mmap_mappings );
63    rtems_semaphore_obtain( rtems_libio_semaphore,
64                            RTEMS_WAIT, RTEMS_NO_TIMEOUT );
65    /* FIXME: account for semaphore in confdefs, or maybe just use the
66     * rtems_libio_semaphore? */
67    if ( mmap_mappings_lock == 0 )
68      sc = rtems_semaphore_create( rtems_build_name( 'M', 'M', 'A', 'P' ),
69                                   1,
70                                   RTEMS_MUTEX_ATTRIBS,
71                                   RTEMS_NO_PRIORITY,
72                                   &mmap_mappings_lock );
73    rtems_semaphore_release( rtems_libio_semaphore );
74    if ( sc != RTEMS_SUCCESSFUL ) {
75      errno = EINVAL;
76      return false;
77    }
78  }
79  return true;
80}
81
82bool mmap_mappings_lock_obtain(
83  void
84)
85{
86  if ( mmap_mappings_lock_create( ) ) {
87    rtems_status_code sc;
88    sc = rtems_semaphore_obtain( mmap_mappings_lock,
89                                 RTEMS_WAIT, RTEMS_NO_TIMEOUT );
90    if ( sc != RTEMS_SUCCESSFUL ) {
91      errno = EINVAL;
92      return false;
93    }
94  }
95  return true;
96}
97
98bool mmap_mappings_lock_release(
99  void
100)
101{
102  rtems_status_code sc;
103  sc = rtems_semaphore_release( mmap_mappings_lock );
104  if (( sc != RTEMS_SUCCESSFUL ) && ( errno == 0 )) {
105    errno = EINVAL;
106    return false;
107  }
108  return true;
109}
110
111void *mmap(
112  void *addr, size_t len, int prot, int flags, int fildes, off_t off
113)
114{
115  struct stat     sb;
116  mmap_mapping   *mapping;
117  mmap_mapping   *current_mapping;
118  ssize_t         r;
119  rtems_libio_t  *iop;
120  bool            map_fixed;
121  bool            map_anonymous;
122  bool            map_shared;
123  bool            map_private;
124  int             err;
125
126  map_fixed = (flags & MAP_FIXED) == MAP_FIXED;
127  map_anonymous = (flags & MAP_ANON) == MAP_ANON;
128  map_shared = (flags & MAP_SHARED) == MAP_SHARED;
129  map_private = (flags & MAP_PRIVATE) == MAP_PRIVATE;
130
131  /* Clear errno. */
132  errno = 0;
133  iop = NULL;
134
135  if ( len == 0 ) {
136    errno = EINVAL;
137    return MAP_FAILED;
138  }
139
140  /*
141   * We can provide read, write and execute because the memory in RTEMS does
142   * not normally have protections but we cannot hide access to memory.
143   */
144  if ( prot == PROT_NONE ) {
145    errno = ENOTSUP;
146    return MAP_FAILED;
147  }
148
149  /*
150   * We can not normally provide restriction of write access. Reject any
151   * attempt to map without write permission, since we are not able to
152   * prevent a write from succeeding.
153   */
154  if ( PROT_WRITE != (prot & PROT_WRITE) ) {
155    errno = ENOTSUP;
156    return MAP_FAILED;
157  }
158
159  /*
160   * Anonymous mappings must have file descriptor set to -1 and the offset
161   * set to 0. Shared mappings are not supported with Anonymous mappings at
162   * this time
163   */
164  if ( map_anonymous && (fildes != -1 || off != 0 || map_shared) ) {
165    errno = EINVAL;
166    return MAP_FAILED;
167  }
168
169  /*
170   * If MAP_ANON is declared without MAP_PRIVATE or MAP_SHARED,
171   * force MAP_PRIVATE
172   */
173  if ( map_anonymous && !map_private && !map_shared ) {
174    flags |= MAP_PRIVATE;
175    map_private = true;
176  }
177
178  /* Check for supported flags */
179  if ((flags & ~(MAP_SHARED | MAP_PRIVATE | MAP_FIXED | MAP_ANON)) != 0) {
180    errno = EINVAL;
181    return MAP_FAILED;
182  }
183
184  /* Either MAP_SHARED or MAP_PRIVATE must be defined, but not both */
185  if ( map_shared ) {
186    if ( map_private ) {
187      errno = EINVAL;
188      return MAP_FAILED;
189    }
190  } else if ( !map_private ) {
191    errno = EINVAL;
192    return MAP_FAILED;
193  }
194
195  /* Check for illegal addresses. Watch out for address wrap. */
196  if ( map_fixed ) {
197    if ((uintptr_t)addr & PAGE_MASK) {
198      errno = EINVAL;
199      return MAP_FAILED;
200    }
201    if ( addr == NULL ) {
202      errno = EINVAL;
203      return MAP_FAILED;
204    }
205    if (addr + len < addr) {
206      errno = EINVAL;
207      return MAP_FAILED;
208    }
209  }
210
211  if ( !map_anonymous ) {
212    /*
213     * Get a stat of the file to get the dev + inode number and to make sure the
214     * fd is ok. The normal libio calls cannot be used because we need to return
215     * MAP_FAILED on error and they return -1 directly without coming back to
216     * here.
217     */
218    if ( fstat( fildes, &sb ) < 0 ) {
219      errno = EBADF;
220      return MAP_FAILED;
221    }
222
223    /* fstat ensures we have a good file descriptor. Hold on to iop. */
224    iop = rtems_libio_iop( fildes );
225
226    /* Check the type of file we have and make sure it is supported. */
227    if ( S_ISDIR( sb.st_mode ) || S_ISLNK( sb.st_mode )) {
228      errno = ENODEV;
229      return MAP_FAILED;
230    }
231
232    /* Check to see if the mapping is valid for a regular file. */
233    if ( S_ISREG( sb.st_mode )
234    /* FIXME: Should this be using strict inequality (>) comparisons? It would
235     * be valid to map a region exactly equal to the st_size, e.g. see below. */
236         && (( off >= sb.st_size ) || (( off + len ) >= sb.st_size ))) {
237      errno = EOVERFLOW;
238      return MAP_FAILED;
239    }
240
241    /* Check to see if the mapping is valid for other file/object types. */
242    if ( !S_ISCHR( sb.st_mode ) && sb.st_size < off + len ) {
243      errno = ENXIO;
244      return MAP_FAILED;
245    }
246
247    /* Do not seek on character devices, pipes, sockets, or memory objects. */
248    if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ) {
249      if ( lseek( fildes, off, SEEK_SET ) < 0 ) {
250        return MAP_FAILED;
251      }
252    }
253
254    /* cdevs do not provide private mappings of any kind. */
255    if ( S_ISCHR( sb.st_mode ) && map_private ) {
256      errno = EINVAL;
257      return MAP_FAILED;
258    }
259  }
260
261  /* Create the mapping */
262  mapping = malloc( sizeof( mmap_mapping ));
263  if ( !mapping ) {
264    errno = ENOMEM;
265    return MAP_FAILED;
266  }
267  memset( mapping, 0, sizeof( mmap_mapping ));
268  mapping->len = len;
269  mapping->flags = flags;
270  mapping->iop = iop;
271
272  if ( !map_anonymous ) {
273    /*
274     * HACK: We should have a better generic way to distinguish between
275     * shm objects and other mmap'd files. We need to know at munmap time
276     * if the mapping is to a shared memory object in order to refcnt shms.
277     * We could do this by providing mmap in the file operations if needed.
278     */
279    if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ||
280         S_ISCHR( sb.st_mode ) || S_ISFIFO( sb.st_mode ) ||
281         S_ISSOCK( sb.st_mode ) ) {
282      mapping->is_shared_shm = false;
283    } else {
284      mapping->is_shared_shm = true;
285    }
286  } else {
287    mapping->is_shared_shm = false;
288  }
289
290  if ( map_fixed ) {
291    mapping->addr = addr;
292  } else if ( map_private ) {
293    /* private mappings of shared memory do not need special treatment. */
294    mapping->is_shared_shm = false;
295    posix_memalign( &mapping->addr, PAGE_SIZE, len );
296    if ( !mapping->addr ) {
297      free( mapping );
298      errno = ENOMEM;
299      return MAP_FAILED;
300    }
301  }
302
303  /* MAP_FIXED is not supported for shared memory objects with MAP_SHARED. */
304  if ( map_fixed && mapping->is_shared_shm ) {
305    free( mapping );
306    errno = ENOTSUP;
307    return MAP_FAILED;
308  }
309
310  /* Lock access to mmap_mappings. Sets errno on failure. */
311  if ( !mmap_mappings_lock_obtain( ) )
312    return MAP_FAILED;
313
314  if ( map_fixed ) {
315    rtems_chain_node* node = rtems_chain_first (&mmap_mappings);
316    while ( !rtems_chain_is_tail( &mmap_mappings, node )) {
317      /*
318       * If the map is fixed see if this address is already mapped. At this
319       * point in time if there is an overlap in the mappings we return an
320       * error. POSIX allows us to also return successfully by unmapping
321       * the overlapping prior mappings.
322       */
323      current_mapping = (mmap_mapping*) node;
324      if ( ( addr >= current_mapping->addr ) &&
325           ( addr < ( current_mapping->addr + current_mapping->len )) ) {
326        free( mapping );
327        mmap_mappings_lock_release( );
328        errno = ENXIO;
329        return MAP_FAILED;
330      }
331      node = rtems_chain_next( node );
332    }
333  }
334
335  /* Populate the data */
336  if ( map_private ) {
337    if ( !map_anonymous ) {
338      /*
339       * Use read() for private mappings. This updates atime as needed.
340       * Changes to the underlying object will NOT be reflected in the mapping.
341       * The underlying object can be removed while the mapping exists.
342       */
343      r = read( fildes, mapping->addr, len );
344
345      if ( r != len ) {
346        mmap_mappings_lock_release( );
347        if ( !map_fixed ) {
348          free( mapping->addr );
349        }
350        free( mapping );
351        errno = ENXIO;
352        return MAP_FAILED;
353      }
354    } else if ( !map_fixed ) {
355      memset( mapping->addr, 0, len );
356    }
357  } else if ( map_shared ) {
358    err = (*iop->pathinfo.handlers->mmap_h)(
359        iop, &mapping->addr, len, prot, off );
360    if ( err != 0 ) {
361      mmap_mappings_lock_release( );
362      free( mapping );
363      return MAP_FAILED;
364    }
365  }
366
367  if ( iop != NULL ) {
368    /* add an extra reference to the file associated with fildes that
369     * is not removed by a subsequent close().  This reference shall be removed
370     * when there are no more mappings to the file. */
371    rtems_libio_increment_mapping_refcnt(iop);
372  }
373
374  rtems_chain_append_unprotected( &mmap_mappings, &mapping->node );
375
376  mmap_mappings_lock_release( );
377
378  return mapping->addr;
379}
Note: See TracBrowser for help on using the repository browser.