[PATCH 2/2] xcb: Use SHM FD-passing if possible

Alexander Volkov <[email protected]>
Newsgroups gmane.comp.lib.cairo
Message-ID <[email protected]>
Shared memory created this way is visible only to the X server
and an application.

- it can used if the X server is running as a non-root user other
  than a user an application is running as
- other applications of the same user can't access it

Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=90512
Signed-off-by: Alexander Volkov <[email protected]>
---
 boilerplate/Makefile.win32.features | 12 ++++
 build/Makefile.win32.features       |  1 +
 build/Makefile.win32.features-h     |  3 +
 build/configure.ac.features         |  1 +
 configure.ac                        | 11 ++++
 src/Makefile.win32.features         | 16 +++++
 src/cairo-xcb-connection-shm.c      | 98 ++++++++++++++++++++++-------
 src/cairo-xcb-connection.c          |  7 ++-
 src/cairo-xcb-private.h             |  4 +-
 9 files changed, 130 insertions(+), 23 deletions(-)

diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index 178d5b650..84df408d1 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -79,6 +79,18 @@ enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xcb_shm_cxx_sources
 enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_shm_sources)
 endif
 
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_shm_fd_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_shm_fd_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_xcb_shm_fd_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xcb_shm_fd_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_shm_fd_sources)
+ifeq ($(CAIRO_HAS_XCB_SHM_FD_FUNCTIONS),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_shm_fd_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_xcb_shm_fd_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xcb_shm_fd_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_shm_fd_sources)
+endif
+
 unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_qt_headers)
 all_cairo_boilerplate_headers += $(cairo_boilerplate_qt_headers)
 all_cairo_boilerplate_private += $(cairo_boilerplate_qt_private)
diff --git a/build/Makefile.win32.features b/build/Makefile.win32.features
index b15c4488f..f8a1e441d 100644
--- a/build/Makefile.win32.features
+++ b/build/Makefile.win32.features
@@ -5,6 +5,7 @@ CAIRO_HAS_XLIB_XRENDER_SURFACE=0
 CAIRO_HAS_XCB_SURFACE=0
 CAIRO_HAS_XLIB_XCB_FUNCTIONS=0
 CAIRO_HAS_XCB_SHM_FUNCTIONS=0
+CAIRO_HAS_XCB_SHM_FD_FUNCTIONS=0
 CAIRO_HAS_QT_SURFACE=0
 CAIRO_HAS_QUARTZ_SURFACE=0
 CAIRO_HAS_QUARTZ_FONT=0
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index 5759b48a3..53984bb4c 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -20,6 +20,9 @@ endif
 ifeq ($(CAIRO_HAS_XCB_SHM_FUNCTIONS),1)
 	@echo "#define CAIRO_HAS_XCB_SHM_FUNCTIONS 1" >> $(top_srcdir)/src/cairo-features.h
 endif
+ifeq ($(CAIRO_HAS_XCB_SHM_FD_FUNCTIONS),1)
+	@echo "#define CAIRO_HAS_XCB_SHM_FD_FUNCTIONS 1" >> $(top_srcdir)/src/cairo-features.h
+endif
 ifeq ($(CAIRO_HAS_QT_SURFACE),1)
 	@echo "#define CAIRO_HAS_QT_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 endif
diff --git a/build/configure.ac.features b/build/configure.ac.features
index e0a46069c..6c8a8f071 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -405,6 +405,7 @@ AC_DEFUN([CAIRO_REPORT],
 	echo "  EGL functions:   $use_egl"
 	echo "  X11-xcb functions: $use_xlib_xcb"
 	echo "  XCB-shm functions: $use_xcb_shm"
+	echo "  XCB-shm FD-passing functions: $use_xcb_shm_fd"
 	echo ""
 	echo "The following features and utilities:"
 	echo "  cairo-trace:                $use_trace"
diff --git a/configure.ac b/configure.ac
index d78b2ed41..8c7fa4267 100644
--- a/configure.ac
+++ b/configure.ac
@@ -182,11 +182,22 @@ CAIRO_ENABLE_FUNCTIONS(xcb_shm, XCB/SHM, auto, [
       xcb_shm_REQUIRES="xcb-shm"
       PKG_CHECK_MODULES(xcb_shm, $xcb_shm_REQUIRES, ,
 			[use_xcb_shm="no (requires $xcb_shm http://xcb.freedesktop.org)"])
+
   else
     use_xcb_shm="no (requires --enable-xcb)"
   fi
 ])
 
+CAIRO_ENABLE_FUNCTIONS(xcb_shm_fd, XCB/SHM FD-passing, auto, [
+  if test "x$use_xcb_shm" = "xyes"; then
+      xcb_shm_fd_REQUIRES="xcb-shm >= 1.9.3"
+      PKG_CHECK_MODULES(xcb_shm_fd, $xcb_shm_fd_REQUIRES, ,
+                        [use_xcb_shm_fd="no (requires $xcb_shm_fd_REQUIRES http://xcb.freedesktop.org)"])
+  else
+      use_xcb_shm_fd="no (requires --enable-xcb-shm)"
+  fi
+])
+
 dnl ===========================================================================
 
 CAIRO_ENABLE_SURFACE_BACKEND(qt, Qt, no, [
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index 377d6dd12..163e2507c 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -101,6 +101,22 @@ ifeq ($(CAIRO_HAS_XCB_SHM_FUNCTIONS),1)
 enabled_cairo_pkgconf += cairo-xcb-shm.pc
 endif
 
+supported_cairo_headers += $(cairo_xcb_shm_fd_headers)
+all_cairo_headers += $(cairo_xcb_shm_fd_headers)
+all_cairo_private += $(cairo_xcb_shm_fd_private)
+all_cairo_cxx_sources += $(cairo_xcb_shm_fd_cxx_sources)
+all_cairo_sources += $(cairo_xcb_shm_fd_sources)
+ifeq ($(CAIRO_HAS_XCB_SHM_FD_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_xcb_shm_fd_headers)
+enabled_cairo_private += $(cairo_xcb_shm_fd_private)
+enabled_cairo_cxx_sources += $(cairo_xcb_shm_fd_cxx_sources)
+enabled_cairo_sources += $(cairo_xcb_shm_fd_sources)
+endif
+all_cairo_pkgconf += cairo-xcb-shm-fd.pc
+ifeq ($(CAIRO_HAS_XCB_SHM_FD_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-xcb-shm-fd.pc
+endif
+
 unsupported_cairo_headers += $(cairo_qt_headers)
 all_cairo_headers += $(cairo_qt_headers)
 all_cairo_private += $(cairo_qt_private)
diff --git a/src/cairo-xcb-connection-shm.c b/src/cairo-xcb-connection-shm.c
index e00377cff..bb73ca9b1 100644
--- a/src/cairo-xcb-connection-shm.c
+++ b/src/cairo-xcb-connection-shm.c
@@ -41,35 +41,86 @@
 #include <sys/shm.h>
 #include <errno.h>
 
+#if CAIRO_HAS_XCB_SHM_FD_FUNCTIONS
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
+
 cairo_int_status_t
 _cairo_xcb_connection_shm_create_segment (cairo_xcb_connection_t *connection,
 				  size_t size,
 				  cairo_bool_t readonly,
 				  cairo_xcb_shm_segment_info_t *shm)
 {
-    xcb_void_cookie_t cookie;
-    xcb_generic_error_t *error;
-
-    shm->shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0600);
-    if (shm->shmid == -1) {
-	return errno == ENOMEM ? CAIRO_STATUS_NO_MEMORY : CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    shm->shm = shmat (shm->shmid, NULL, 0);
-    shmctl (shm->shmid, IPC_RMID, NULL);
-    if (unlikely (shm->shm == (char *) -1))
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
-    shm->shmseg = _cairo_xcb_connection_get_xid (connection);
-    cookie = xcb_shm_attach_checked (connection->xcb_connection, shm->shmseg, shm->shmid, readonly);
-    error = xcb_request_check (connection->xcb_connection, cookie);
-    if (error != NULL) {
-	_cairo_xcb_connection_put_xid (connection, shm->shmseg);
-	shmdt (shm->shm);
-	free (error);
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+#if CAIRO_HAS_XCB_SHM_FD_FUNCTIONS
+    if (connection->flags & CAIRO_XCB_HAS_SHM_FD) {
+	xcb_shm_create_segment_cookie_t cookie;
+	xcb_shm_create_segment_reply_t *reply;
+	xcb_generic_error_t *error;
+	int *fds;
+	int err;
+
+	if (unlikely (size > UINT32_MAX))
+	    return CAIRO_STATUS_NO_MEMORY;
+
+	shm->shmseg = _cairo_xcb_connection_get_xid (connection);
+	cookie = xcb_shm_create_segment (connection->xcb_connection, shm->shmseg, size, FALSE);
+	reply = xcb_shm_create_segment_reply (connection->xcb_connection, cookie, &error);
+	if (error) {
+	    free (error);
+	    _cairo_xcb_connection_put_xid (connection, shm->shmseg);
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	}
+
+	fds = xcb_shm_create_segment_reply_fds (connection->xcb_connection, reply);
+	if (reply->nfd != 1) {
+	    for (int i = 0; i < reply->nfd; i++)
+		close (fds[i]);
+
+	    free (reply);
+	    xcb_shm_detach (connection->xcb_connection, shm->shmseg);
+	    _cairo_xcb_connection_put_xid (connection, shm->shmseg);
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	}
+
+	shm->shm = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fds[0], 0);
+	err = errno;
+	close (fds[0]);
+	free (reply);
+
+	if (shm->shm == MAP_FAILED) {
+	    xcb_shm_detach (connection->xcb_connection, shm->shmseg);
+	    _cairo_xcb_connection_put_xid (connection, shm->shmseg);
+	    return err == ENOMEM ? CAIRO_STATUS_NO_MEMORY : CAIRO_INT_STATUS_UNSUPPORTED;
+	}
+    } else
+#endif
+    {
+	xcb_void_cookie_t cookie;
+	xcb_generic_error_t *error;
+
+	shm->shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0600);
+	if (shm->shmid == -1) {
+	    return errno == ENOMEM ? CAIRO_STATUS_NO_MEMORY : CAIRO_INT_STATUS_UNSUPPORTED;
+	}
+
+	shm->shm = shmat (shm->shmid, NULL, 0);
+	shmctl (shm->shmid, IPC_RMID, NULL);
+	if (unlikely (shm->shm == (char *) -1))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	shm->shmseg = _cairo_xcb_connection_get_xid (connection);
+	cookie = xcb_shm_attach_checked (connection->xcb_connection, shm->shmseg, shm->shmid, readonly);
+	error = xcb_request_check (connection->xcb_connection, cookie);
+	if (error != NULL) {
+	    _cairo_xcb_connection_put_xid (connection, shm->shmseg);
+	    shmdt (shm->shm);
+	    free (error);
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	}
     }
 
+    shm->size = size;
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
@@ -79,6 +130,11 @@ _cairo_xcb_connection_shm_destroy_segment (cairo_xcb_connection_t *connection,
 {
     xcb_shm_detach (connection->xcb_connection, shm->shmseg);
     _cairo_xcb_connection_put_xid (connection, shm->shmseg);
+#if CAIRO_HAS_XCB_SHM_FD_FUNCTIONS
+    if (connection->flags & CAIRO_XCB_HAS_SHM_FD)
+	munmap (shm->shm, shm->size);
+    else
+#endif
     shmdt (shm->shm);
 }
 
diff --git a/src/cairo-xcb-connection.c b/src/cairo-xcb-connection.c
index 67e2b11a0..2f7645cbe 100644
--- a/src/cairo-xcb-connection.c
+++ b/src/cairo-xcb-connection.c
@@ -453,12 +453,17 @@ _cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection)
     if (version == NULL)
 	return;
 
+    connection->flags |= CAIRO_XCB_HAS_SHM;
+    if ((version->major_version == 1 && version->minor_version >= 2) ||
+	version->major_version > 1)
+	connection->flags |= CAIRO_XCB_HAS_SHM_FD;
+
     free (version);
 
-    connection->flags |= CAIRO_XCB_HAS_SHM;
     status = _cairo_xcb_connection_shm_create_segment (connection, 0x1000, FALSE, &seg_info);
     if (status != CAIRO_INT_STATUS_SUCCESS) {
 	connection->flags &= ~CAIRO_XCB_HAS_SHM;
+	connection->flags &= ~CAIRO_XCB_HAS_SHM_FD;
 	return;
     }
 
diff --git a/src/cairo-xcb-private.h b/src/cairo-xcb-private.h
index 4e417ddff..8a3b9715a 100644
--- a/src/cairo-xcb-private.h
+++ b/src/cairo-xcb-private.h
@@ -82,6 +82,7 @@ struct _cairo_xcb_shm_segment_info {
     int shmid;
     uint32_t shmseg;
     void *shm;
+    size_t size;
 };
 
 struct _cairo_xcb_shm_info {
@@ -271,7 +272,8 @@ enum {
     CAIRO_XCB_RENDER_HAS_FILTER_GOOD		= 0x0400,
     CAIRO_XCB_RENDER_HAS_FILTER_BEST		= 0x0800,
 
-    CAIRO_XCB_HAS_SHM				= 0x80000000,
+    CAIRO_XCB_HAS_SHM				= 0x40000000,
+    CAIRO_XCB_HAS_SHM_FD			= 0x80000000,
 
     CAIRO_XCB_RENDER_MASK = CAIRO_XCB_HAS_RENDER |
 			    CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES |
-- 
2.17.0

-- 
cairo mailing list
[email protected]
https://lists.cairographics.org/mailman/listinfo/cairo
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.