LCOV - code coverage report
Current view: top level - port - cpl_userfaultfd.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 116 226 51.3 %
Date: 2025-07-09 17:50:03 Functions: 6 7 85.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     cpl_userfaultfd.cpp
       4             :  * Project:  CPL - Common Portability Library
       5             :  * Purpose:  Use userfaultfd and VSIL to service page faults
       6             :  * Author:   James McClain, <james.mcclain@gmail.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2018, Dr. James McClain <james.mcclain@gmail.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #ifdef ENABLE_UFFD
      15             : 
      16             : #include <algorithm>
      17             : #include <cstdlib>
      18             : #include <cinttypes>
      19             : #include <cstring>
      20             : #include <string>
      21             : 
      22             : #include <errno.h>
      23             : #include <fcntl.h>
      24             : #include <poll.h>
      25             : #include <pthread.h>
      26             : #include <sched.h>
      27             : #include <signal.h>
      28             : #include <unistd.h>
      29             : 
      30             : #include <sys/ioctl.h>
      31             : #include <sys/mman.h>
      32             : #include <sys/stat.h>
      33             : #include <sys/syscall.h>
      34             : #include <sys/types.h>
      35             : #include <sys/utsname.h>
      36             : #include <linux/userfaultfd.h>
      37             : 
      38             : #include "cpl_conv.h"
      39             : #include "cpl_error.h"
      40             : #include "cpl_userfaultfd.h"
      41             : #include "cpl_string.h"
      42             : #include "cpl_vsi.h"
      43             : #include "cpl_multiproc.h"
      44             : 
      45             : #ifndef UFFD_USER_MODE_ONLY
      46             : // The UFFD_USER_MODE_ONLY flag got added in kernel 5.11 which is the one
      47             : // used by Ubuntu 20.04, but the linux-libc-dev package corresponds to 5.4
      48             : #define UFFD_USER_MODE_ONLY 1
      49             : #endif
      50             : 
      51             : #define BAD_MMAP (reinterpret_cast<void *>(-1))
      52             : #define MAX_MESSAGES (0x100)
      53             : 
      54             : static int64_t get_page_limit();
      55             : static void cpl_uffd_fault_handler(void *ptr);
      56             : static void signal_handler(int signal);
      57             : static void uffd_cleanup(void *ptr);
      58             : 
      59             : struct cpl_uffd_context
      60             : {
      61             :     bool keep_going = false;
      62             : 
      63             :     int uffd = -1;
      64             :     struct uffdio_register uffdio_register = {};
      65             :     struct uffd_msg uffd_msgs[MAX_MESSAGES];
      66             : 
      67             :     std::string filename = std::string("");
      68             : 
      69             :     int64_t page_limit = -1;
      70             :     int64_t pages_used = 0;
      71             : 
      72             :     size_t file_size = 0;
      73             :     size_t page_size = 0;
      74             :     void *page_ptr = nullptr;
      75             :     size_t vma_size = 0;
      76             :     void *vma_ptr = nullptr;
      77             :     CPLJoinableThread *thread = nullptr;
      78             : };
      79             : 
      80           3 : static void uffd_cleanup(void *ptr)
      81             : {
      82           3 :     struct cpl_uffd_context *ctx = static_cast<struct cpl_uffd_context *>(ptr);
      83             : 
      84           3 :     if (!ctx)
      85           0 :         return;
      86             : 
      87             :     // Signal shutdown
      88           3 :     ctx->keep_going = false;
      89           3 :     if (ctx->thread)
      90             :     {
      91           3 :         CPLJoinThread(ctx->thread);
      92           3 :         ctx->thread = nullptr;
      93             :     }
      94             : 
      95           3 :     if (ctx->uffd != -1)
      96             :     {
      97           3 :         ioctl(ctx->uffd, UFFDIO_UNREGISTER, &ctx->uffdio_register);
      98           3 :         close(ctx->uffd);
      99           3 :         ctx->uffd = -1;
     100             :     }
     101           3 :     if (ctx->page_ptr && ctx->page_size)
     102           3 :         munmap(ctx->page_ptr, ctx->page_size);
     103           3 :     if (ctx->vma_ptr && ctx->vma_size)
     104           3 :         munmap(ctx->vma_ptr, ctx->vma_size);
     105           3 :     ctx->page_ptr = nullptr;
     106           3 :     ctx->vma_ptr = nullptr;
     107           3 :     ctx->page_size = 0;
     108           3 :     ctx->vma_size = 0;
     109           3 :     ctx->pages_used = 0;
     110           3 :     ctx->page_limit = 0;
     111             : 
     112           3 :     delete ctx;
     113             : 
     114           3 :     return;
     115             : }
     116             : 
     117             : #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
     118             : #pragma GCC diagnostic push
     119             : #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
     120             : #endif
     121             : static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
     122             : #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
     123             : #pragma GCC diagnostic pop
     124             : #endif
     125             : 
     126           3 : static int64_t get_page_limit()
     127             : {
     128             :     int64_t retval;
     129           3 :     const char *variable = CPLGetConfigOption(GDAL_UFFD_LIMIT, nullptr);
     130             : 
     131           3 :     if (variable && sscanf(variable, "%" PRId64, &retval) == 1)
     132           0 :         return retval;
     133             :     else
     134           3 :         return -1;
     135             : }
     136             : 
     137           3 : static void cpl_uffd_fault_handler(void *ptr)
     138             : {
     139           3 :     struct cpl_uffd_context *ctx = static_cast<struct cpl_uffd_context *>(ptr);
     140             :     struct uffdio_copy uffdio_copy;
     141             :     struct pollfd pollfd;
     142             : 
     143             :     // Setup pollfd structure
     144           3 :     pollfd.fd = ctx->uffd;
     145           3 :     pollfd.events = POLLIN;
     146             : 
     147             :     // Open asset for reading
     148           3 :     VSILFILE *file = VSIFOpenL(ctx->filename.c_str(), "rb");
     149             : 
     150           3 :     if (!file)
     151           0 :         return;
     152             : 
     153             :     // Loop until told to stop
     154          24 :     while (ctx->keep_going)
     155             :     {
     156             :         // Poll for event
     157          21 :         if (poll(&pollfd, 1, 16) == -1)
     158           0 :             break;  // 60Hz when no demand
     159          21 :         if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLNVAL))
     160             :             break;
     161          21 :         if (!(pollfd.revents & POLLIN))
     162          18 :             continue;
     163             : 
     164             :         // Read page fault events
     165             :         ssize_t bytes_read = static_cast<ssize_t>(
     166           3 :             read(ctx->uffd, ctx->uffd_msgs, MAX_MESSAGES * sizeof(uffd_msg)));
     167           3 :         if (bytes_read < 1)
     168             :         {
     169           0 :             if (errno == EWOULDBLOCK)
     170           0 :                 continue;
     171             :             else
     172           0 :                 break;
     173             :         }
     174             : 
     175             :         // If too many pages are in use, evict all pages (evict them from
     176             :         // RAM and swap, not just to swap).  It is impossible to control
     177             :         // which/when threads access the VMA, so access to the VMA has to
     178             :         // forbidden while the activity is in progress.
     179             :         //
     180             :         // That is done by (1) installing special handlers for SIGSEGV and
     181             :         // SIGBUS, (2) mprotecting the VMA so that any threads accessing
     182             :         // it receive either SIGSEGV or SIGBUS (which one is apparently a
     183             :         // function of the C library, at least on one non-Linux GNU
     184             :         // system[1]), (3) unregistering the VMA from userfaultfd,
     185             :         // remapping the VMA to evict the pages, registering the VMA
     186             :         // again, (4) making the VMA accessible again, and finally (5)
     187             :         // restoring the previous signal-handling behavior.
     188             :         //
     189             :         // [1] https://lists.debian.org/debian-bsd/2011/05/msg00032.html
     190           3 :         if (ctx->page_limit > 0)
     191             :         {
     192           0 :             pthread_mutex_lock(&mutex);
     193           0 :             if (ctx->pages_used > ctx->page_limit)
     194             :             {
     195             :                 struct sigaction segv;
     196             :                 struct sigaction old_segv;
     197             :                 struct sigaction bus;
     198             :                 struct sigaction old_bus;
     199             : 
     200           0 :                 memset(&segv, 0, sizeof(segv));
     201           0 :                 memset(&old_segv, 0, sizeof(old_segv));
     202           0 :                 memset(&bus, 0, sizeof(bus));
     203           0 :                 memset(&old_bus, 0, sizeof(old_bus));
     204             : 
     205             :                 // Step 1 from the block comment above
     206           0 :                 segv.sa_handler = signal_handler;
     207           0 :                 bus.sa_handler = signal_handler;
     208           0 :                 if (sigaction(SIGSEGV, &segv, &old_segv) == -1)
     209             :                 {
     210           0 :                     CPLError(
     211             :                         CE_Failure, CPLE_AppDefined,
     212             :                         "cpl_uffd_fault_handler: sigaction(SIGSEGV) failed");
     213           0 :                     pthread_mutex_unlock(&mutex);
     214           0 :                     break;
     215             :                 }
     216           0 :                 if (sigaction(SIGBUS, &bus, &old_bus) == -1)
     217             :                 {
     218           0 :                     CPLError(
     219             :                         CE_Failure, CPLE_AppDefined,
     220             :                         "cpl_uffd_fault_handler: sigaction(SIGBUS) failed");
     221           0 :                     pthread_mutex_unlock(&mutex);
     222           0 :                     break;
     223             :                 }
     224             : 
     225             :                 // WARNING: LACK OF THREAD-SAFETY.
     226             :                 //
     227             :                 // For example, if a user program (or another part of the
     228             :                 // library) installs a SIGSEGV or SIGBUS handler from another
     229             :                 // thread after this one has installed its handlers but before
     230             :                 // this one uninstalls its handlers, the intervening handler
     231             :                 // will be eliminated.  There are other examples, as well, but
     232             :                 // there can only be a problems with other threads because the
     233             :                 // faulting thread is blocked here.
     234             :                 //
     235             :                 // This implies that one should not use cpl_virtualmem.h API
     236             :                 // while other threads are actively generating faults that use
     237             :                 // this mechanism.
     238             :                 //
     239             :                 // Having multiple active threads that use this mechanism but
     240             :                 // with no changes to signal-handling in other threads is NOT a
     241             :                 // problem.
     242             : 
     243             :                 // Step 2
     244           0 :                 if (mprotect(ctx->vma_ptr, ctx->vma_size, PROT_NONE) == -1)
     245             :                 {
     246           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     247             :                              "cpl_uffd_fault_handler: mprotect() failed");
     248           0 :                     pthread_mutex_unlock(&mutex);
     249           0 :                     break;
     250             :                 }
     251             : 
     252             :                 // Step 3
     253           0 :                 if (ioctl(ctx->uffd, UFFDIO_UNREGISTER, &ctx->uffdio_register))
     254             :                 {
     255           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     256             :                              "cpl_uffd_fault_handler: ioctl(UFFDIO_UNREGISTER) "
     257             :                              "failed");
     258           0 :                     pthread_mutex_unlock(&mutex);
     259           0 :                     break;
     260             :                 }
     261           0 :                 ctx->vma_ptr =
     262           0 :                     mmap(ctx->vma_ptr, ctx->vma_size, PROT_NONE,
     263             :                          MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
     264           0 :                 if (ctx->vma_ptr == BAD_MMAP)
     265             :                 {
     266           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     267             :                              "cpl_uffd_fault_handler: mmap() failed");
     268           0 :                     ctx->vma_ptr = nullptr;
     269           0 :                     pthread_mutex_unlock(&mutex);
     270           0 :                     break;
     271             :                 }
     272           0 :                 ctx->pages_used = 0;
     273           0 :                 if (ioctl(ctx->uffd, UFFDIO_REGISTER, &ctx->uffdio_register))
     274             :                 {
     275           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     276             :                              "cpl_uffd_fault_handler: ioctl(UFFDIO_REGISTER) "
     277             :                              "failed");
     278           0 :                     pthread_mutex_unlock(&mutex);
     279           0 :                     break;
     280             :                 }
     281             : 
     282             :                 // Step 4.  Problem: A thread might attempt to read here (before
     283             :                 // the mprotect) and receive a SIGSEGV or SIGBUS.
     284           0 :                 if (mprotect(ctx->vma_ptr, ctx->vma_size, PROT_READ) == -1)
     285             :                 {
     286           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     287             :                              "cpl_uffd_fault_handler: mprotect() failed");
     288           0 :                     pthread_mutex_unlock(&mutex);
     289           0 :                     break;
     290             :                 }
     291             : 
     292             :                 // Step 5.  Solution: Cannot unregister special handlers before
     293             :                 // any such threads have been handled by them, so sleep for
     294             :                 // 1/100th of a second.
     295             :                 // Coverity complains about sleeping under a mutex
     296             : #ifndef __COVERITY__
     297             :                 // coverity[sleep]
     298           0 :                 usleep(10000);
     299             : #endif
     300           0 :                 if (sigaction(SIGSEGV, &old_segv, nullptr) == -1)
     301             :                 {
     302           0 :                     CPLError(
     303             :                         CE_Failure, CPLE_AppDefined,
     304             :                         "cpl_uffd_fault_handler: sigaction(SIGSEGV) failed");
     305           0 :                     pthread_mutex_unlock(&mutex);
     306           0 :                     break;
     307             :                 }
     308           0 :                 if (sigaction(SIGBUS, &old_bus, nullptr) == -1)
     309             :                 {
     310           0 :                     CPLError(
     311             :                         CE_Failure, CPLE_AppDefined,
     312             :                         "cpl_uffd_fault_handler: sigaction(SIGBUS) failed");
     313           0 :                     pthread_mutex_unlock(&mutex);
     314           0 :                     break;
     315             :                 }
     316             :             }
     317           0 :             pthread_mutex_unlock(&mutex);
     318             :         }
     319             : 
     320             :         // Handle page fault events
     321           6 :         for (int i = 0; i < static_cast<int>(bytes_read / sizeof(uffd_msg));
     322             :              ++i)
     323             :         {
     324           3 :             const uintptr_t fault_addr =
     325           3 :                 ctx->uffd_msgs[i].arg.pagefault.address & ~(ctx->page_size - 1);
     326           3 :             const uintptr_t offset =
     327           3 :                 fault_addr - reinterpret_cast<uintptr_t>(ctx->vma_ptr);
     328           3 :             size_t bytes_needed = static_cast<size_t>(ctx->file_size - offset);
     329           3 :             if (bytes_needed > ctx->page_size)
     330           0 :                 bytes_needed = ctx->page_size;
     331             : 
     332             :             // Copy data into page
     333           6 :             if (VSIFSeekL(file, offset, SEEK_SET) != 0 ||
     334           3 :                 VSIFReadL(ctx->page_ptr, bytes_needed, 1, file) != 1)
     335             :             {
     336           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     337             :                          "Cannot get %d bytes at offset " CPL_FRMT_GUIB " of "
     338             :                          "file %s",
     339             :                          static_cast<int>(bytes_needed),
     340             :                          static_cast<GUIntBig>(offset), ctx->filename.c_str());
     341           0 :                 memset(ctx->page_ptr, 0, bytes_needed);
     342             :             }
     343           3 :             ctx->pages_used++;
     344             : 
     345             :             // Use the page to fulfill the page fault
     346           3 :             uffdio_copy.src = reinterpret_cast<uintptr_t>(ctx->page_ptr);
     347           3 :             uffdio_copy.dst = fault_addr;
     348           3 :             uffdio_copy.len = static_cast<uintptr_t>(ctx->page_size);
     349           3 :             uffdio_copy.mode = 0;
     350           3 :             uffdio_copy.copy = 0;
     351           3 :             if (ioctl(ctx->uffd, UFFDIO_COPY, &uffdio_copy) == -1)
     352             :             {
     353           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     354             :                          "ioctl(UFFDIO_COPY) failed");
     355           0 :                 break;
     356             :             }
     357             :         }
     358             :     }  // end of while loop
     359             : 
     360             :     // Return resources
     361           3 :     VSIFCloseL(file);
     362             : }
     363             : 
     364           0 : static void signal_handler(int signal)
     365             : {
     366           0 :     if (signal == SIGSEGV || signal == SIGBUS)
     367           0 :         sched_yield();
     368           0 :     return;
     369             : }
     370             : 
     371          18 : bool CPLIsUserFaultMappingSupported()
     372             : {
     373             :     // Check the Linux kernel version.  Linux 4.3 or newer is needed for
     374             :     // userfaultfd.
     375          18 :     int major = 0, minor = 0;
     376             :     struct utsname utsname;
     377             : 
     378          18 :     if (uname(&utsname))
     379           0 :         return false;
     380          18 :     sscanf(utsname.release, "%d.%d", &major, &minor);
     381          18 :     if (major < 4)
     382           0 :         return false;
     383          18 :     if (major == 4 && minor < 3)
     384           0 :         return false;
     385             : 
     386             :     static int nEnableUserFaultFD = -1;
     387          18 :     if (nEnableUserFaultFD < 0)
     388             :     {
     389          12 :         nEnableUserFaultFD =
     390          12 :             CPLTestBool(CPLGetConfigOption("CPL_ENABLE_USERFAULTFD", "YES"));
     391             :     }
     392          18 :     if (!nEnableUserFaultFD)
     393           0 :         return false;
     394             : 
     395             :     // Since kernel 5.2, raw userfaultfd is disabled since if the fault
     396             :     // originates from the kernel, that could lead to easier exploitation of
     397             :     // kernel bugs. Since kernel 5.11, UFFD_USER_MODE_ONLY can be used to
     398             :     // restrict the mechanism to faults occurring only from user space, which is
     399             :     // likely to be our use case.
     400          18 :     int uffd = static_cast<int>(syscall(
     401          18 :         __NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY));
     402          18 :     if (uffd == -1 && errno == EINVAL)
     403           0 :         uffd =
     404           0 :             static_cast<int>(syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK));
     405          18 :     if (uffd == -1)
     406             :     {
     407           0 :         const int l_errno = errno;
     408           0 :         if (l_errno == EPERM)
     409             :         {
     410             :             // Since kernel 5.2
     411           0 :             CPLDebug(
     412             :                 "GDAL",
     413             :                 "CPLIsUserFaultMappingSupported(): syscall(__NR_userfaultfd) "
     414             :                 "failed: "
     415             :                 "insufficient permission. add CAP_SYS_PTRACE capability, or "
     416             :                 "set /proc/sys/vm/unprivileged_userfaultfd to 1");
     417             :         }
     418             :         else
     419             :         {
     420           0 :             CPLDebug(
     421             :                 "GDAL",
     422             :                 "CPLIsUserFaultMappingSupported(): syscall(__NR_userfaultfd) "
     423             :                 "failed: "
     424             :                 "error = %d",
     425             :                 l_errno);
     426             :         }
     427           0 :         nEnableUserFaultFD = false;
     428           0 :         return false;
     429             :     }
     430          18 :     close(uffd);
     431          18 :     nEnableUserFaultFD = true;
     432          18 :     return true;
     433             : }
     434             : 
     435             : /*
     436             :  * Returns nullptr on failure, a valid pointer on success.
     437             :  */
     438           3 : cpl_uffd_context *CPLCreateUserFaultMapping(const char *pszFilename,
     439             :                                             void **ppVma, uint64_t *pnVmaSize)
     440             : {
     441             :     VSIStatBufL statbuf;
     442           3 :     struct cpl_uffd_context *ctx = nullptr;
     443             : 
     444           3 :     if (!CPLIsUserFaultMappingSupported())
     445             :     {
     446           0 :         CPLError(
     447             :             CE_Failure, CPLE_NotSupported,
     448             :             "CPLCreateUserFaultMapping(): Linux kernel 4.3 or newer needed");
     449           0 :         return nullptr;
     450             :     }
     451             : 
     452             :     // Get the size of the asset
     453           3 :     if (VSIStatL(pszFilename, &statbuf))
     454           0 :         return nullptr;
     455             : 
     456             :     // Setup the `cpl_uffd_context` struct
     457           3 :     ctx = new cpl_uffd_context();
     458           3 :     ctx->keep_going = true;
     459           3 :     ctx->filename = std::string(pszFilename);
     460           3 :     ctx->page_limit = get_page_limit();
     461           3 :     ctx->pages_used = 0;
     462           3 :     ctx->file_size = static_cast<size_t>(statbuf.st_size);
     463           3 :     ctx->page_size = static_cast<size_t>(std::max(1L, sysconf(_SC_PAGESIZE)));
     464           3 :     ctx->vma_size = static_cast<size_t>(
     465           3 :         ((static_cast<vsi_l_offset>(statbuf.st_size) / ctx->page_size) + 1) *
     466           3 :         ctx->page_size);
     467           3 :     if (ctx->vma_size < static_cast<vsi_l_offset>(statbuf.st_size))
     468             :     {  // Check for overflow
     469           0 :         uffd_cleanup(ctx);
     470           0 :         CPLError(
     471             :             CE_Failure, CPLE_AppDefined,
     472             :             "CPLCreateUserFaultMapping(): File too large for architecture");
     473           0 :         return nullptr;
     474             :     }
     475             : 
     476             :     // If the mmap failed, free resources and return
     477           3 :     ctx->vma_ptr = mmap(nullptr, ctx->vma_size, PROT_READ,
     478             :                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     479           3 :     if (ctx->vma_ptr == BAD_MMAP)
     480             :     {
     481           0 :         ctx->vma_ptr = nullptr;
     482           0 :         uffd_cleanup(ctx);
     483           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     484             :                  "CPLCreateUserFaultMapping(): mmap() failed");
     485           0 :         return nullptr;
     486             :     }
     487             : 
     488             :     // Attempt to acquire a scratch page to use to fulfill requests.
     489           3 :     ctx->page_ptr =
     490           3 :         mmap(nullptr, static_cast<size_t>(ctx->page_size),
     491             :              PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     492           3 :     if (ctx->page_ptr == BAD_MMAP)
     493             :     {
     494           0 :         ctx->page_ptr = nullptr;
     495           0 :         uffd_cleanup(ctx);
     496           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     497             :                  "CPLCreateUserFaultMapping(): mmap() failed");
     498           0 :         return nullptr;
     499             :     }
     500             : 
     501             :     // Get userfaultfd
     502             : 
     503             :     // Since kernel 5.2, raw userfaultfd is disabled since if the fault
     504             :     // originates from the kernel, that could lead to easier exploitation of
     505             :     // kernel bugs. Since kernel 5.11, UFFD_USER_MODE_ONLY can be used to
     506             :     // restrict the mechanism to faults occurring only from user space, which is
     507             :     // likely to be our use case.
     508           3 :     ctx->uffd = static_cast<int>(syscall(
     509             :         __NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY));
     510           3 :     if (ctx->uffd == -1 && errno == EINVAL)
     511           0 :         ctx->uffd =
     512           0 :             static_cast<int>(syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK));
     513           3 :     if (ctx->uffd == -1)
     514             :     {
     515           0 :         const int l_errno = errno;
     516           0 :         ctx->uffd = -1;
     517           0 :         uffd_cleanup(ctx);
     518           0 :         if (l_errno == EPERM)
     519             :         {
     520             :             // Since kernel 5.2
     521           0 :             CPLError(
     522             :                 CE_Failure, CPLE_AppDefined,
     523             :                 "CPLCreateUserFaultMapping(): syscall(__NR_userfaultfd) "
     524             :                 "failed: "
     525             :                 "insufficient permission. add CAP_SYS_PTRACE capability, or "
     526             :                 "set /proc/sys/vm/unprivileged_userfaultfd to 1");
     527             :         }
     528             :         else
     529             :         {
     530           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     531             :                      "CPLCreateUserFaultMapping(): syscall(__NR_userfaultfd) "
     532             :                      "failed: "
     533             :                      "error = %d",
     534             :                      l_errno);
     535             :         }
     536           0 :         return nullptr;
     537             :     }
     538             : 
     539             :     // Query API
     540             :     {
     541           3 :         struct uffdio_api uffdio_api = {};
     542             : 
     543           3 :         uffdio_api.api = UFFD_API;
     544           3 :         uffdio_api.features = 0;
     545             : 
     546           3 :         if (ioctl(ctx->uffd, UFFDIO_API, &uffdio_api) == -1)
     547             :         {
     548           0 :             uffd_cleanup(ctx);
     549           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     550             :                      "CPLCreateUserFaultMapping(): ioctl(UFFDIO_API) failed");
     551           0 :             return nullptr;
     552             :         }
     553             :     }
     554             : 
     555             :     // Register memory range
     556           3 :     ctx->uffdio_register.range.start =
     557           3 :         reinterpret_cast<uintptr_t>(ctx->vma_ptr);
     558           3 :     ctx->uffdio_register.range.len = ctx->vma_size;
     559           3 :     ctx->uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
     560             : 
     561           3 :     if (ioctl(ctx->uffd, UFFDIO_REGISTER, &ctx->uffdio_register) == -1)
     562             :     {
     563           0 :         uffd_cleanup(ctx);
     564           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     565             :                  "CPLCreateUserFaultMapping(): ioctl(UFFDIO_REGISTER) failed");
     566           0 :         return nullptr;
     567             :     }
     568             : 
     569             :     // Start handler thread
     570           3 :     ctx->thread = CPLCreateJoinableThread(cpl_uffd_fault_handler, ctx);
     571           3 :     if (ctx->thread == nullptr)
     572             :     {
     573           0 :         CPLError(
     574             :             CE_Failure, CPLE_AppDefined,
     575             :             "CPLCreateUserFaultMapping(): CPLCreateJoinableThread() failed");
     576           0 :         uffd_cleanup(ctx);
     577           0 :         return nullptr;
     578             :     }
     579             : 
     580           3 :     *ppVma = ctx->vma_ptr;
     581           3 :     *pnVmaSize = ctx->vma_size;
     582           3 :     return ctx;
     583             : }
     584             : 
     585         661 : void CPLDeleteUserFaultMapping(cpl_uffd_context *ctx)
     586             : {
     587         661 :     if (ctx)
     588             :     {
     589           3 :         uffd_cleanup(ctx);
     590             :     }
     591         661 : }
     592             : 
     593             : #endif  // ENABLE_UFFD

Generated by: LCOV version 1.14