LCOV - code coverage report
Current view: top level - port - cpl_userfaultfd.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 117 227 51.5 %
Date: 2026-02-12 06:20:29 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           3 :             if (VSIFSeekL(file, static_cast<vsi_l_offset>(offset), SEEK_SET) !=
     334           6 :                     0 ||
     335           3 :                 VSIFReadL(ctx->page_ptr, bytes_needed, 1, file) != 1)
     336             :             {
     337           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     338             :                          "Cannot get %d bytes at offset " CPL_FRMT_GUIB " of "
     339             :                          "file %s",
     340             :                          static_cast<int>(bytes_needed),
     341             :                          static_cast<GUIntBig>(offset), ctx->filename.c_str());
     342           0 :                 memset(ctx->page_ptr, 0, bytes_needed);
     343             :             }
     344           3 :             ctx->pages_used++;
     345             : 
     346             :             // Use the page to fulfill the page fault
     347           3 :             uffdio_copy.src = reinterpret_cast<uintptr_t>(ctx->page_ptr);
     348           3 :             uffdio_copy.dst = fault_addr;
     349           3 :             uffdio_copy.len = static_cast<uintptr_t>(ctx->page_size);
     350           3 :             uffdio_copy.mode = 0;
     351           3 :             uffdio_copy.copy = 0;
     352           3 :             if (ioctl(ctx->uffd, UFFDIO_COPY, &uffdio_copy) == -1)
     353             :             {
     354           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     355             :                          "ioctl(UFFDIO_COPY) failed");
     356           0 :                 break;
     357             :             }
     358             :         }
     359             :     }  // end of while loop
     360             : 
     361             :     // Return resources
     362           3 :     VSIFCloseL(file);
     363             : }
     364             : 
     365           0 : static void signal_handler(int signal)
     366             : {
     367           0 :     if (signal == SIGSEGV || signal == SIGBUS)
     368           0 :         sched_yield();
     369           0 :     return;
     370             : }
     371             : 
     372          18 : bool CPLIsUserFaultMappingSupported()
     373             : {
     374             :     // Check the Linux kernel version.  Linux 4.3 or newer is needed for
     375             :     // userfaultfd.
     376          18 :     int major = 0, minor = 0;
     377             :     struct utsname utsname;
     378             : 
     379          18 :     if (uname(&utsname))
     380           0 :         return false;
     381          18 :     sscanf(utsname.release, "%d.%d", &major, &minor);
     382          18 :     if (major < 4)
     383           0 :         return false;
     384          18 :     if (major == 4 && minor < 3)
     385           0 :         return false;
     386             : 
     387             :     static int nEnableUserFaultFD = -1;
     388          18 :     if (nEnableUserFaultFD < 0)
     389             :     {
     390          12 :         nEnableUserFaultFD =
     391          12 :             CPLTestBool(CPLGetConfigOption("CPL_ENABLE_USERFAULTFD", "YES"));
     392             :     }
     393          18 :     if (!nEnableUserFaultFD)
     394           0 :         return false;
     395             : 
     396             :     // Since kernel 5.2, raw userfaultfd is disabled since if the fault
     397             :     // originates from the kernel, that could lead to easier exploitation of
     398             :     // kernel bugs. Since kernel 5.11, UFFD_USER_MODE_ONLY can be used to
     399             :     // restrict the mechanism to faults occurring only from user space, which is
     400             :     // likely to be our use case.
     401          18 :     int uffd = static_cast<int>(syscall(
     402          18 :         __NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY));
     403          18 :     if (uffd == -1 && errno == EINVAL)
     404           0 :         uffd =
     405           0 :             static_cast<int>(syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK));
     406          18 :     if (uffd == -1)
     407             :     {
     408           0 :         const int l_errno = errno;
     409           0 :         if (l_errno == EPERM)
     410             :         {
     411             :             // Since kernel 5.2
     412           0 :             CPLDebug(
     413             :                 "GDAL",
     414             :                 "CPLIsUserFaultMappingSupported(): syscall(__NR_userfaultfd) "
     415             :                 "failed: "
     416             :                 "insufficient permission. add CAP_SYS_PTRACE capability, or "
     417             :                 "set /proc/sys/vm/unprivileged_userfaultfd to 1");
     418             :         }
     419             :         else
     420             :         {
     421           0 :             CPLDebug(
     422             :                 "GDAL",
     423             :                 "CPLIsUserFaultMappingSupported(): syscall(__NR_userfaultfd) "
     424             :                 "failed: "
     425             :                 "error = %d",
     426             :                 l_errno);
     427             :         }
     428           0 :         nEnableUserFaultFD = false;
     429           0 :         return false;
     430             :     }
     431          18 :     close(uffd);
     432          18 :     nEnableUserFaultFD = true;
     433          18 :     return true;
     434             : }
     435             : 
     436             : /*
     437             :  * Returns nullptr on failure, a valid pointer on success.
     438             :  */
     439           3 : cpl_uffd_context *CPLCreateUserFaultMapping(const char *pszFilename,
     440             :                                             void **ppVma, uint64_t *pnVmaSize)
     441             : {
     442             :     VSIStatBufL statbuf;
     443           3 :     struct cpl_uffd_context *ctx = nullptr;
     444             : 
     445           3 :     if (!CPLIsUserFaultMappingSupported())
     446             :     {
     447           0 :         CPLError(
     448             :             CE_Failure, CPLE_NotSupported,
     449             :             "CPLCreateUserFaultMapping(): Linux kernel 4.3 or newer needed");
     450           0 :         return nullptr;
     451             :     }
     452             : 
     453             :     // Get the size of the asset
     454           3 :     if (VSIStatL(pszFilename, &statbuf))
     455           0 :         return nullptr;
     456             : 
     457             :     // Setup the `cpl_uffd_context` struct
     458           3 :     ctx = new cpl_uffd_context();
     459           3 :     ctx->keep_going = true;
     460           3 :     ctx->filename = std::string(pszFilename);
     461           3 :     ctx->page_limit = get_page_limit();
     462           3 :     ctx->pages_used = 0;
     463           3 :     ctx->file_size = static_cast<size_t>(statbuf.st_size);
     464           3 :     ctx->page_size = static_cast<size_t>(std::max(1L, sysconf(_SC_PAGESIZE)));
     465           3 :     ctx->vma_size = static_cast<size_t>(
     466           3 :         ((static_cast<vsi_l_offset>(statbuf.st_size) / ctx->page_size) + 1) *
     467           3 :         ctx->page_size);
     468           3 :     if (ctx->vma_size < static_cast<vsi_l_offset>(statbuf.st_size))
     469             :     {  // Check for overflow
     470           0 :         uffd_cleanup(ctx);
     471           0 :         CPLError(
     472             :             CE_Failure, CPLE_AppDefined,
     473             :             "CPLCreateUserFaultMapping(): File too large for architecture");
     474           0 :         return nullptr;
     475             :     }
     476             : 
     477             :     // If the mmap failed, free resources and return
     478           3 :     ctx->vma_ptr = mmap(nullptr, ctx->vma_size, PROT_READ,
     479             :                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     480           3 :     if (ctx->vma_ptr == BAD_MMAP)
     481             :     {
     482           0 :         ctx->vma_ptr = nullptr;
     483           0 :         uffd_cleanup(ctx);
     484           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     485             :                  "CPLCreateUserFaultMapping(): mmap() failed");
     486           0 :         return nullptr;
     487             :     }
     488             : 
     489             :     // Attempt to acquire a scratch page to use to fulfill requests.
     490           3 :     ctx->page_ptr =
     491           3 :         mmap(nullptr, static_cast<size_t>(ctx->page_size),
     492             :              PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     493           3 :     if (ctx->page_ptr == BAD_MMAP)
     494             :     {
     495           0 :         ctx->page_ptr = nullptr;
     496           0 :         uffd_cleanup(ctx);
     497           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     498             :                  "CPLCreateUserFaultMapping(): mmap() failed");
     499           0 :         return nullptr;
     500             :     }
     501             : 
     502             :     // Get userfaultfd
     503             : 
     504             :     // Since kernel 5.2, raw userfaultfd is disabled since if the fault
     505             :     // originates from the kernel, that could lead to easier exploitation of
     506             :     // kernel bugs. Since kernel 5.11, UFFD_USER_MODE_ONLY can be used to
     507             :     // restrict the mechanism to faults occurring only from user space, which is
     508             :     // likely to be our use case.
     509           3 :     ctx->uffd = static_cast<int>(syscall(
     510             :         __NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY));
     511           3 :     if (ctx->uffd == -1 && errno == EINVAL)
     512           0 :         ctx->uffd =
     513           0 :             static_cast<int>(syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK));
     514           3 :     if (ctx->uffd == -1)
     515             :     {
     516           0 :         const int l_errno = errno;
     517           0 :         ctx->uffd = -1;
     518           0 :         uffd_cleanup(ctx);
     519           0 :         if (l_errno == EPERM)
     520             :         {
     521             :             // Since kernel 5.2
     522           0 :             CPLError(
     523             :                 CE_Failure, CPLE_AppDefined,
     524             :                 "CPLCreateUserFaultMapping(): syscall(__NR_userfaultfd) "
     525             :                 "failed: "
     526             :                 "insufficient permission. add CAP_SYS_PTRACE capability, or "
     527             :                 "set /proc/sys/vm/unprivileged_userfaultfd to 1");
     528             :         }
     529             :         else
     530             :         {
     531           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     532             :                      "CPLCreateUserFaultMapping(): syscall(__NR_userfaultfd) "
     533             :                      "failed: "
     534             :                      "error = %d",
     535             :                      l_errno);
     536             :         }
     537           0 :         return nullptr;
     538             :     }
     539             : 
     540             :     // Query API
     541             :     {
     542           3 :         struct uffdio_api uffdio_api = {};
     543             : 
     544           3 :         uffdio_api.api = UFFD_API;
     545           3 :         uffdio_api.features = 0;
     546             : 
     547           3 :         if (ioctl(ctx->uffd, UFFDIO_API, &uffdio_api) == -1)
     548             :         {
     549           0 :             uffd_cleanup(ctx);
     550           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     551             :                      "CPLCreateUserFaultMapping(): ioctl(UFFDIO_API) failed");
     552           0 :             return nullptr;
     553             :         }
     554             :     }
     555             : 
     556             :     // Register memory range
     557           3 :     ctx->uffdio_register.range.start =
     558           3 :         reinterpret_cast<uintptr_t>(ctx->vma_ptr);
     559           3 :     ctx->uffdio_register.range.len = ctx->vma_size;
     560           3 :     ctx->uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
     561             : 
     562           3 :     if (ioctl(ctx->uffd, UFFDIO_REGISTER, &ctx->uffdio_register) == -1)
     563             :     {
     564           0 :         uffd_cleanup(ctx);
     565           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     566             :                  "CPLCreateUserFaultMapping(): ioctl(UFFDIO_REGISTER) failed");
     567           0 :         return nullptr;
     568             :     }
     569             : 
     570             :     // Start handler thread
     571           3 :     ctx->thread = CPLCreateJoinableThread(cpl_uffd_fault_handler, ctx);
     572           3 :     if (ctx->thread == nullptr)
     573             :     {
     574           0 :         CPLError(
     575             :             CE_Failure, CPLE_AppDefined,
     576             :             "CPLCreateUserFaultMapping(): CPLCreateJoinableThread() failed");
     577           0 :         uffd_cleanup(ctx);
     578           0 :         return nullptr;
     579             :     }
     580             : 
     581           3 :     *ppVma = ctx->vma_ptr;
     582           3 :     *pnVmaSize = ctx->vma_size;
     583           3 :     return ctx;
     584             : }
     585             : 
     586         670 : void CPLDeleteUserFaultMapping(cpl_uffd_context *ctx)
     587             : {
     588         670 :     if (ctx)
     589             :     {
     590           3 :         uffd_cleanup(ctx);
     591             :     }
     592         670 : }
     593             : 
     594             : #endif  // ENABLE_UFFD

Generated by: LCOV version 1.14