source: rtems/cpukit/libdl/rtl-obj-cache.c @ 58c34961

5
Last change on this file since 58c34961 was 58c34961, checked in by Chris Johns <chrisj@…>, on 08/12/16 at 08:02:52

libdl: Fix cache corruption bugs.

This patch fixes a number of bugs in the cache when requests are
made to read close to the end of the file and the data is copied
from the top of the cache buffer to the bottom of the buffer. This
was compounded by attempting to read past the end of the file.

Closes #2754.

  • Property mode set to 100644
File size: 6.7 KB
Line 
1/*
2 *  COPYRIGHT (c) 2012 Chris Johns <chrisj@rtems.org>
3 *
4 *  The license and distribution terms for this file may be
5 *  found in the file LICENSE in this distribution or at
6 *  http://www.rtems.org/license/LICENSE.
7 */
8/**
9 * @file
10 *
11 * @ingroup rtems_rtl
12 *
13 * @brief RTEMS Run-Time Linker Object File cache buffers a section of the
14 *        object file in a buffer to localise read performance.
15 */
16
17#if HAVE_CONFIG_H
18#include "config.h"
19#endif
20
21#include <errno.h>
22#include <stdio.h>
23#include <string.h>
24#include <unistd.h>
25
26#include <rtems/rtl/rtl-allocator.h>
27#include "rtl-obj-cache.h"
28#include "rtl-error.h"
29#include "rtl-trace.h"
30
31bool
32rtems_rtl_obj_cache_open (rtems_rtl_obj_cache_t* cache, size_t size)
33{
34  cache->fd        = -1;
35  cache->file_size = 0;
36  cache->offset    = 0;
37  cache->size      = size;
38  cache->level     = 0;
39  cache->buffer    = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, size, false);
40  if (!cache->buffer)
41  {
42    rtems_rtl_set_error (ENOMEM, "no memory for cache buffer");
43    return false;
44  }
45  return true;
46}
47
48void
49rtems_rtl_obj_cache_close (rtems_rtl_obj_cache_t* cache)
50{
51  if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
52    printf ("rtl: cache: %2d: close\n", cache->fd);
53  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, cache->buffer);
54  cache->buffer    = NULL;
55  cache->fd        = -1;
56  cache->file_size = 0;
57  cache->level     = 0;
58}
59
60void
61rtems_rtl_obj_cache_flush (rtems_rtl_obj_cache_t* cache)
62{
63  if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
64    printf ("rtl: cache: %2d: flush\n", cache->fd);
65  cache->fd        = -1;
66  cache->file_size = 0;
67  cache->offset    = 0;
68  cache->level     = 0;
69}
70
71bool
72rtems_rtl_obj_cache_read (rtems_rtl_obj_cache_t* cache,
73                          int                    fd,
74                          off_t                  offset,
75                          void**                 buffer,
76                          size_t*                length)
77{
78  struct stat sb;
79
80  if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
81    printf ("rtl: cache: %2d: fd=%d offset=%d length=%d area=[%d,%d] cache=[%d,%d] size=%d\n",
82            fd, cache->fd, (int) offset, (int) *length,
83            (int) offset, (int) offset + *length,
84            (int) cache->offset, (int) cache->offset + cache->level,
85            (int) cache->file_size);
86
87  if (*length > cache->size)
88  {
89    rtems_rtl_set_error (EINVAL, "read size larger than cache size");
90    return false;
91  }
92
93  if (cache->fd == fd)
94  {
95    if (offset > cache->file_size)
96    {
97      rtems_rtl_set_error (EINVAL, "offset past end of file: offset=%i size=%i",
98                           (int) offset, (int) cache->file_size);
99      return false;
100    }
101
102    /*
103     * We sometimes are asked to read strings of a length we do not know.
104     */
105    if ((offset + *length) > cache->file_size)
106    {
107      *length = cache->file_size - offset;
108      if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
109        printf ("rtl: cache: %2d: truncate length=%d\n", fd, (int) *length);
110    }
111  }
112
113  while (true)
114  {
115    size_t buffer_offset = 0;
116    size_t buffer_read = cache->size;
117
118    /*
119     * Is the data in the cache for this file ?
120     */
121    if (fd == cache->fd)
122    {
123      /*
124       * Do not read past the end of the file.
125       */
126      if ((offset + buffer_read) > cache->file_size)
127        buffer_read = cache->file_size - offset;
128
129      /*
130       * Is any part of the data in the cache ?
131       */
132      if ((offset >= cache->offset) &&
133          (offset < (cache->offset + cache->level)))
134      {
135        size_t size;
136
137        buffer_offset = offset - cache->offset;
138        size          = cache->level - buffer_offset;
139
140        /*
141         * Return the location of the data in the cache.
142         */
143        *buffer = cache->buffer + buffer_offset;
144
145        /*
146         * Is all the data in the cache or just a part ?
147         */
148        if (*length <= size)
149          return true;
150
151        if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
152          printf ("rtl: cache: %2d: copy-down: buffer_offset=%d size=%d level=%d\n",
153                  fd, (int) buffer_offset, (int) size, (int) cache->level);
154
155        /*
156         * Copy down the data in the buffer and then fill the remaining space
157         * with as much data we are able to read.
158         */
159        memmove (cache->buffer, cache->buffer + buffer_offset, size);
160
161        cache->offset = offset;
162        cache->level  = size;
163        buffer_read   = cache->size - cache->level;
164        buffer_offset = size;
165
166        /*
167         * Do not read past the end of the file.
168         */
169        if ((offset + buffer_offset + buffer_read) > cache->file_size)
170          buffer_read = cache->file_size - (offset + buffer_offset);
171      }
172    }
173
174    if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
175      printf ("rtl: cache: %2d: seek: offset=%d buffer_offset=%d read=%d cache=[%d,%d] dist=%d\n",
176              fd, (int) offset + buffer_offset, (int) buffer_offset, (int) buffer_read,
177              (int) offset, (int) offset + buffer_read,
178              (int) (cache->file_size - offset));
179
180    if (lseek (fd, offset + buffer_offset, SEEK_SET) < 0)
181    {
182      rtems_rtl_set_error (errno, "file seek failed");
183      return false;
184    }
185
186    /*
187     * Loop reading the data from the file until either an error or 0 is
188     * returned and if data has been read check if the amount is what we
189     * want. If not it is an error. A POSIX read can read data in fragments.
190     */
191
192    cache->level = buffer_offset + buffer_read;
193
194    while (buffer_read)
195    {
196      int r = read (fd, cache->buffer + buffer_offset, buffer_read);
197      if (r < 0)
198      {
199        rtems_rtl_set_error (errno, "file read failed");
200        return false;
201      }
202      if ((r == 0) && buffer_read)
203      {
204        if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
205          printf ("rtl: cache: %2d: read: past end by=%d\n", fd, (int) buffer_read);
206        cache->level = cache->level - buffer_read;
207        buffer_read = 0;
208      }
209      else
210      {
211        buffer_read -= r;
212        buffer_offset += r;
213      }
214    }
215
216    cache->offset = offset;
217
218    if (cache->fd != fd)
219    {
220      cache->fd = fd;
221
222      if (fstat (cache->fd, &sb) < 0)
223      {
224        rtems_rtl_set_error (errno, "file stat failed");
225        return false;
226      }
227
228      cache->file_size = sb.st_size;
229    }
230  }
231
232  return false;
233}
234
235bool
236rtems_rtl_obj_cache_read_byval (rtems_rtl_obj_cache_t* cache,
237                                int                    fd,
238                                off_t                  offset,
239                                void*                  buffer,
240                                size_t                 length)
241{
242  void*  cbuffer = 0;
243  size_t len = length;
244  bool   ok = rtems_rtl_obj_cache_read (cache, fd, offset, &cbuffer, &len);
245  if (ok && (len != length))
246    ok = false;
247  if (ok)
248    memcpy (buffer, cbuffer, length);
249  return ok;
250}
Note: See TracBrowser for help on using the repository browser.