libexplain
1.4.D001
|
00001 /* 00002 * libexplain - Explain errno values returned by libc functions 00003 * Copyright (C) 2011-2013 Peter Miller 00004 * 00005 * This program is free software; you can redistribute it and/or modify it 00006 * under the terms of the GNU Lesser General Public License as published by 00007 * the Free Software Foundation; either version 3 of the License, or (at 00008 * your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, but 00011 * WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 00013 * General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 #include <libexplain/ac/errno.h> 00020 #include <libexplain/ac/stdlib.h> 00021 #include <libexplain/ac/string.h> 00022 #include <libexplain/ac/sys/mman.h> 00023 #include <libexplain/ac/sys/shm.h> 00024 #include <libexplain/ac/unistd.h> 00025 00026 #include <libexplain/buffer/eacces.h> 00027 #include <libexplain/buffer/einval.h> 00028 #include <libexplain/buffer/enomem.h> 00029 #include <libexplain/buffer/errno/generic.h> 00030 #include <libexplain/buffer/errno/shmat.h> 00031 #include <libexplain/buffer/gettext.h> 00032 #include <libexplain/buffer/int.h> 00033 #include <libexplain/buffer/is_the_null_pointer.h> 00034 #include <libexplain/buffer/must_be_multiple_of_page_size.h> 00035 #include <libexplain/buffer/pointer.h> 00036 #include <libexplain/buffer/shmflg.h> 00037 #include <libexplain/explanation.h> 00038 #include <libexplain/getpagesize.h> 00039 #include <libexplain/translate.h> 00040 00041 00042 static void 00043 explain_buffer_errno_shmat_system_call(explain_string_buffer_t *sb, int errnum, 00044 int shmid, const void *shmaddr, int shmflg) 00045 { 00046 (void)errnum; 00047 explain_string_buffer_puts(sb, "shmat(shmid = "); 00048 explain_buffer_int(sb, shmid); 00049 explain_string_buffer_puts(sb, ", shmaddr = "); 00050 explain_buffer_pointer(sb, shmaddr); 00051 explain_string_buffer_puts(sb, ", shmflg = "); 00052 explain_buffer_shmflg(sb, shmflg); 00053 explain_string_buffer_putc(sb, ')'); 00054 } 00055 00056 #ifdef HAVE_MINCORE 00057 00058 static int 00059 memory_already_mapped(const void *data, size_t data_size) 00060 { 00061 int page_size; 00062 unsigned char *vec; 00063 00064 page_size = explain_getpagesize(); 00065 if (page_size < 0) 00066 return 0; 00067 vec = malloc((data_size + page_size - 1) / page_size); 00068 if (!vec) 00069 return 0; 00070 /* 00071 * The signed-ness of 'vec' differs by system. There is a 50/50 00072 * chance you will get a compiler warning on the next line. 00073 */ 00074 if (mincore((void *)data, data_size, vec) < 0) 00075 { 00076 free(vec); 00077 /* 00078 * Strictly speaking, only ENOMEM indicates that one or more 00079 * pages were not mapped. 00080 */ 00081 return 0; 00082 } 00083 free(vec); 00084 return 1; 00085 } 00086 00087 #endif 00088 00089 static int 00090 shmid_exists(int shmid) 00091 { 00092 struct shmid_ds shm; 00093 00094 return 00095 ( 00096 shmctl(shmid, IPC_STAT, &shm) >= 0 00097 || 00098 errno == EACCES 00099 || 00100 errno == EPERM 00101 ); 00102 } 00103 00104 00105 static int 00106 wrap_shmctl(int shmid, int cmd, struct shmid_ds *data) 00107 { 00108 if (shmctl(shmid, cmd, data) >= 0) 00109 return 0; 00110 if (errno == EACCES || errno == EPERM) 00111 { 00112 /* 00113 * Note that shmctl(IPC_STAT) requires read permission, so if we 00114 * get EACCES that means we know we have no read permission, but 00115 * we don't know anything else. Fake it. 00116 */ 00117 memset(data, 0, sizeof(*data)); 00118 return 0; 00119 } 00120 return -1; 00121 } 00122 00123 00124 void 00125 explain_buffer_errno_shmat_explanation(explain_string_buffer_t *sb, int errnum, 00126 const char *syscall_name, int shmid, const void *shmaddr, int shmflg) 00127 { 00128 /* 00129 * http://www.opengroup.org/onlinepubs/009695399/functions/shmat.html 00130 */ 00131 switch (errnum) 00132 { 00133 case EACCES: 00134 case EPERM: 00135 /* 00136 * The calling process does not have the required permissions for 00137 * the requested attach type, and does not have the CAP_IPC_OWNER 00138 * capability. 00139 */ 00140 { 00141 struct shmid_ds shm; 00142 00143 if (wrap_shmctl(shmid, IPC_STAT, &shm) >= 0) 00144 { 00145 int read_only; 00146 00147 read_only = !!(shmflg & SHM_RDONLY); 00148 if (explain_buffer_eacces_shmat(sb, &shm.shm_perm, read_only)) 00149 return; 00150 } 00151 } 00152 00153 /* 00154 * We will have to settle for being vague. 00155 */ 00156 explain_buffer_eacces_shmat_vague(sb); 00157 break; 00158 00159 case EINVAL: 00160 #ifdef SHM_REMAP 00161 if ((shmflg & SHM_REMAP) && !shmaddr) 00162 { 00163 explain_buffer_is_the_null_pointer(sb, "shmaddr"); 00164 return; 00165 } 00166 #endif 00167 /* use shmctl to find this out */ 00168 if (!shmid_exists(shmid)) 00169 { 00170 explain_string_buffer_printf_gettext 00171 ( 00172 sb, 00173 /* 00174 * xgettext: This error message is used when explaining that a 00175 * IPC object does not exist, when it should. 00176 */ 00177 i18n("the %s does not exist"), 00178 explain_translate_shared_memory_segment() 00179 ); 00180 return; 00181 } 00182 #ifdef HAVE_GETPAGESIZE 00183 if (shmaddr && !(shmflg & SHM_RND)) 00184 { 00185 int page_size; 00186 00187 page_size = explain_getpagesize(); 00188 if 00189 ( 00190 page_size > 0 00191 && 00192 ((size_t)shmaddr % (size_t)page_size) != 0 00193 ) 00194 { 00195 explain_buffer_must_be_multiple_of_page_size(sb, "shmaddr"); 00196 return; 00197 } 00198 } 00199 #endif 00200 #ifdef HAVE_MINCORE 00201 { 00202 struct shmid_ds shm; 00203 00204 /* 00205 * The shmctl system call requires that the caller must have 00206 * read permission on the shared memory segment, so we may 00207 * not be able to be helpful if they aren't the owner (but 00208 * the we shouldn't get to here, that would be EACCES). 00209 */ 00210 if 00211 ( 00212 shmctl(shmid, IPC_STAT, &shm) >= 0 00213 && 00214 memory_already_mapped(shmaddr, shm.shm_segsz) 00215 ) 00216 { 00217 explain_string_buffer_printf 00218 ( 00219 sb, 00220 /* FIXME: i18n */ 00221 "virtual memory has already been mapped at the " 00222 "address indicated by the %s argument", 00223 "shmaddr" 00224 ); 00225 return; 00226 } 00227 } 00228 #endif 00229 00230 /* Nothing obvious... */ 00231 goto generic; 00232 00233 case ENOMEM: /* Linux */ 00234 case EMFILE: /* POSIX */ 00235 /* 00236 * See if the process's maximum number of mappings would have 00237 * been exceeded. 00238 */ 00239 { 00240 struct shmid_ds shm; 00241 00242 /* 00243 * The shmctl system call required that the caller must have 00244 * read permission on the shared memory segment. 00245 */ 00246 if 00247 ( 00248 shmctl(shmid, IPC_STAT, &shm) >= 0 00249 && 00250 explain_buffer_enomem_rlimit_exceeded(sb, shm.shm_segsz) 00251 ) 00252 { 00253 return; 00254 } 00255 } 00256 00257 /* 00258 * Could not allocate memory 00259 * (a) for the descriptor, or 00260 * (b) for the page tables. 00261 */ 00262 explain_buffer_enomem_kernel(sb); 00263 break; 00264 00265 default: 00266 generic: 00267 explain_buffer_errno_generic(sb, errnum, syscall_name); 00268 break; 00269 } 00270 } 00271 00272 00273 void 00274 explain_buffer_errno_shmat(explain_string_buffer_t *sb, int errnum, int shmid, 00275 const void *shmaddr, int shmflg) 00276 { 00277 explain_explanation_t exp; 00278 00279 explain_explanation_init(&exp, errnum); 00280 explain_buffer_errno_shmat_system_call(&exp.system_call_sb, errnum, shmid, 00281 shmaddr, shmflg); 00282 explain_buffer_errno_shmat_explanation(&exp.explanation_sb, errnum, "shmat", 00283 shmid, shmaddr, shmflg); 00284 explain_explanation_assemble(&exp, sb); 00285 } 00286 00287 00288 /* vim: set ts=8 sw=4 et : */