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

5
Last change on this file since bb01a36 was bb01a36, checked in by Kevin Kirspel <kevin-kirspel@…>, on 07/19/17 at 14:59:16

Fixed issue with searching mapped addresses

The loop that checks if the current address is already mapped uses
the same local variable for the chanin node as the newly allocated
chain node so the allocated chain node gets over written.

Added a new local variable for the loop that checks the address

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( &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.