mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() and its caller chain
@ 2026-09-08 21:16 Mauricio Faria de Oliveira
  2026-09-08 21:16 ` [PATCH 1/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() Mauricio Faria de Oliveira
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Mauricio Faria de Oliveira @ 2026-09-08 21:16 UTC (permalink / raw)
  To: Andreas Gruenbacher
  Cc: kernel-dev, gfs2, linux-kernel, Mauricio Faria de Oliveira

Currently, gfs2_getbuf() will hit a kernel oops for dereferencing an error
pointer if __filemap_get_folio() with FGP_CREAT fails, eg ERR_PTR(-ENOMEM),
as there is no error handling in this case.

The code and documentation in filemap.c and pagemap.h show that it indeed
may fail, and other callers in the kernel check for errors with FGP_CREAT.

This series adds such error handling in gfs2_getbuf() and its caller chain.

Tested with fstests on v7.3-rc1. It "Failed 51 out 788 tests" consistently,
plus one (different) flaky test on ~10 runs before and after this series.

Thanks,

Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
---
Mauricio Faria de Oliveira (5):
      gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf()
      gfs2: handle NULL return value in callers of gfs2_getbuf() with CREATE
      gfs2: handle NULL return value in callers of gfs2_meta_new()
      gfs2: handle NULL return value in callers of gfs2_meta_ra()
      gfs2: check for errors from filemap_get_folio() in gfs2_jhead_process_page()

 fs/gfs2/bmap.c     | 27 +++++++++++++++++++--------
 fs/gfs2/dir.c      |  8 ++++++++
 fs/gfs2/inode.c    | 20 +++++++++++++++-----
 fs/gfs2/lops.c     | 31 ++++++++++++++++++++++++++-----
 fs/gfs2/meta_io.c  | 30 +++++++++++++++++++++++-------
 fs/gfs2/recovery.c |  2 ++
 fs/gfs2/xattr.c    |  6 ++++++
 7 files changed, 99 insertions(+), 25 deletions(-)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260908-gfs2_getbuf-error-checks-e8f89a08acc4

Best regards,
-- 
Mauricio Faria de Oliveira <mfo@igalia.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf()
  2026-09-08 21:16 [PATCH 0/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() and its caller chain Mauricio Faria de Oliveira
@ 2026-09-08 21:16 ` Mauricio Faria de Oliveira
  2026-09-08 21:16 ` [PATCH 2/5] gfs2: handle NULL return value in callers of gfs2_getbuf() with CREATE Mauricio Faria de Oliveira
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Mauricio Faria de Oliveira @ 2026-09-08 21:16 UTC (permalink / raw)
  To: Andreas Gruenbacher
  Cc: kernel-dev, gfs2, linux-kernel, Mauricio Faria de Oliveira

Function __filemap_get_folio() with FGP_CREAT might fail, for example, with
ERR_PTR(-ENOMEM). This hits a kernel oops, as gfs2_getbuf() dereferences an
error pointer:

 gfs2: fsid=loop0: Trying to join cluster "lock_nolock", "loop0"
 gfs2: fsid=loop0: Now mounting FS (format 1802)...
 Oops: general protection fault, probably for non-canonical address
                      0xdffffc0000000003: 0000 [#1] SMP KASAN NOPTI
 KASAN: null-ptr-deref in range [0x0000000000000018-0x000000000000001f]
 ...
 RIP: 0010:gfs2_getbuf+0x492/0x5a0
 [  ...
 Call Trace:
  <TASK>
  gfs2_meta_read+0x106/0x7a0
  gfs2_meta_buffer+0x125/0x360
  inode_go_instantiate+0xde/0x1510
  gfs2_instantiate+0x102/0x1c0
  gfs2_glock_wait+0x19a/0x2c0
  gfs2_lookupi+0x2ba/0x570
  gfs2_lookup_meta+0xad/0x160
  init_inodes+0x350/0x20b0
  gfs2_fill_super+0x19b6/0x2730
  get_tree_bdev_flags+0x2f4/0x560
  gfs2_get_tree+0x48/0x230
  vfs_get_tree+0x87/0x2d0
  vfs_cmd_create+0xb2/0x240
  __do_sys_fsconfig+0x3d2/0x980
  do_syscall_64+0xce/0x450
  entry_SYSCALL_64_after_hwframe+0x77/0x7f
 ...
  </TASK>

That is noted more clearly in convenience function filemap_grab_folio()
(snippet below) and checked by virtually all other callers in the kernel.

 ...
  * Return: A found or created folio. ERR_PTR(-ENOMEM) if no folio is found
  * and failed to create a folio.
 ...
 static inline struct folio *filemap_grab_folio(...)
 {
         return __filemap_get_folio(...,
                                    ... | FGP_CREAT,
                                    ...);
 }

Thus, if __filemap_get_folio() returns an error, log it (as it's lost) and
return NULL. Callers will be handled.

Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
---
 fs/gfs2/meta_io.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index a87cfbf0df38706e6b7c63cc55f5251765d6b398..51b1a2e2bfadea6dabfac5ecba983c615a1a69fa 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -145,6 +145,10 @@ struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno, int create)
 		folio = __filemap_get_folio(mapping, index,
 				FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
 				mapping_gfp_mask(mapping) | __GFP_NOFAIL);
+		if (IS_ERR(folio)) {
+			fs_err(sdp, "Error %ld creating buffer\n", PTR_ERR(folio));
+			return NULL;
+		}
 		bh = folio_buffers(folio);
 		if (!bh)
 			bh = create_empty_buffers(folio,

-- 
2.47.3


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/5] gfs2: handle NULL return value in callers of gfs2_getbuf() with CREATE
  2026-09-08 21:16 [PATCH 0/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() and its caller chain Mauricio Faria de Oliveira
  2026-09-08 21:16 ` [PATCH 1/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() Mauricio Faria de Oliveira
@ 2026-09-08 21:16 ` Mauricio Faria de Oliveira
  2026-09-08 21:16 ` [PATCH 3/5] gfs2: handle NULL return value in callers of gfs2_meta_new() Mauricio Faria de Oliveira
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Mauricio Faria de Oliveira @ 2026-09-08 21:16 UTC (permalink / raw)
  To: Andreas Gruenbacher
  Cc: kernel-dev, gfs2, linux-kernel, Mauricio Faria de Oliveira

Now that gfs2_getbuf() with CREATE can return NULL (instead of hit a kernel
oops), update its callers to handle it (and not hit another kernel oops).

If gfs2_getbuf() returns NULL:

- gfs2_metapath_ra()
- gfs2_dir_readahead()

  Skip the rest of the loop. This should be OK as it's currently done if
  !trylock_buffer() or buffer_uptodate(), anyway.
  We can skip put_bh() as gfs2_getbuf() doesn't do get_bh() in this case.

- gfs2_meta_new()

  Propagate NULL. Callers will be handled.

- gfs2_meta_read()

  For either a block or read-ahead, fail as currently done by other cases.
  (Factor out the error path used multiple times now.)

- gfs2_meta_ra()

  - For the first buffer:

    Popagate NULL. Callers will be handled.

  - For remaining buffers:

    Skip the rest of the loop. This should be OK as the condition to exit
    the loop is based on the first buffer (not remaining buffers), anyway.

Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
---
 fs/gfs2/bmap.c    |  2 ++
 fs/gfs2/dir.c     |  2 ++
 fs/gfs2/meta_io.c | 26 +++++++++++++++++++-------
 3 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 73c62697116390f0f5f09f0b8fd267cc0790e953..a766b5816634d360a4bbae89f8bcdd284071864a 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -302,6 +302,8 @@ static void gfs2_metapath_ra(struct gfs2_glock *gl, __be64 *start, __be64 *end)
 			continue;
 
 		rabh = gfs2_getbuf(gl, be64_to_cpu(*t), CREATE);
+		if (!rabh)
+			continue;
 		if (trylock_buffer(rabh)) {
 			if (!buffer_uptodate(rabh)) {
 				bh_submit(rabh,
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 0237b36b9eb16562ab3a3415b53547c43ab26f52..0d92f6c33dcb85dfa2a4b07f9f09575834e75e01 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -1502,6 +1502,8 @@ static void gfs2_dir_readahead(struct inode *inode, unsigned hsize, u32 index,
 			continue;
 
 		bh = gfs2_getbuf(gl, blocknr, 1);
+		if (!bh)
+			continue;
 		if (trylock_buffer(bh)) {
 			if (buffer_uptodate(bh)) {
 				unlock_buffer(bh);
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index 51b1a2e2bfadea6dabfac5ecba983c615a1a69fa..daa38fc7c825cdb70349caa7b55ef333b10af7c2 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -199,6 +199,8 @@ struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno)
 {
 	struct buffer_head *bh;
 	bh = gfs2_getbuf(gl, blkno, CREATE);
+	if (!bh)
+		return NULL;
 	meta_prep_new(bh);
 	return bh;
 }
@@ -268,12 +270,13 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
 	struct buffer_head *bh, *bhs[2];
 	int num = 0;
 
-	if (gfs2_withdrawn(sdp)) {
-		*bhp = NULL;
-		return -EIO;
-	}
+	if (gfs2_withdrawn(sdp))
+		goto error;
 
-	*bhp = bh = gfs2_getbuf(gl, blkno, CREATE);
+	bh = gfs2_getbuf(gl, blkno, CREATE);
+	if (!bh)
+		goto error;
+	*bhp = bh;
 
 	lock_buffer(bh);
 	if (buffer_uptodate(bh)) {
@@ -286,6 +289,8 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
 
 	if (rahead) {
 		bh = gfs2_getbuf(gl, blkno + 1, CREATE);
+		if (!bh)
+			goto error;
 
 		lock_buffer(bh);
 		if (buffer_uptodate(bh)) {
@@ -307,11 +312,14 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
 		if (tr && test_bit(TR_TOUCHED, &tr->tr_flags))
 			gfs2_io_error_bh(sdp, bh);
 		brelse(bh);
-		*bhp = NULL;
-		return -EIO;
+		goto error;
 	}
 
 	return 0;
+
+error:
+	*bhp = NULL;
+	return -EIO;
 }
 
 /**
@@ -494,6 +502,8 @@ struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen)
 		extlen = max_ra;
 
 	first_bh = gfs2_getbuf(gl, dblock, CREATE);
+	if (!first_bh)
+		return NULL;
 
 	if (buffer_uptodate(first_bh))
 		goto out;
@@ -504,6 +514,8 @@ struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen)
 
 	while (extlen) {
 		bh = gfs2_getbuf(gl, dblock, CREATE);
+		if (!bh)
+			continue;
 
 		bh_readahead(bh, REQ_RAHEAD | REQ_META | REQ_PRIO);
 		brelse(bh);

-- 
2.47.3


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 3/5] gfs2: handle NULL return value in callers of gfs2_meta_new()
  2026-09-08 21:16 [PATCH 0/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() and its caller chain Mauricio Faria de Oliveira
  2026-09-08 21:16 ` [PATCH 1/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() Mauricio Faria de Oliveira
  2026-09-08 21:16 ` [PATCH 2/5] gfs2: handle NULL return value in callers of gfs2_getbuf() with CREATE Mauricio Faria de Oliveira
@ 2026-09-08 21:16 ` Mauricio Faria de Oliveira
  2026-09-08 21:16 ` [PATCH 4/5] gfs2: handle NULL return value in callers of gfs2_meta_ra() Mauricio Faria de Oliveira
  2026-09-08 21:16 ` [PATCH 5/5] gfs2: check for errors from filemap_get_folio() in gfs2_jhead_process_page() Mauricio Faria de Oliveira
  4 siblings, 0 replies; 6+ messages in thread
From: Mauricio Faria de Oliveira @ 2026-09-08 21:16 UTC (permalink / raw)
  To: Andreas Gruenbacher
  Cc: kernel-dev, gfs2, linux-kernel, Mauricio Faria de Oliveira

Now that gfs2_meta_new() can return NULL (instead of hit a kernel oops),
update its callers to handle it (and not hit another kernel oops).

If gfs2_meta_new() returns NULL:

- new_leaf()

  It currently checks for NULL and returns -EIO.

- gfs2_indirect_init()

  Return -EIO. The only caller is __gfs2_iomap_alloc() (same file), twice.
  Fail as currently done in other cases.

- gfs2_dir_get_new_buffer()

  Return -EIO. The only two callers already check for errors.

- gfs2_init_xattr()
- init_dinode()

  Return -EIO. The only caller is gfs2_create_inode() (same file).
  Fail as currently done in the preceding error case.

- buf_lo_scan_elements()
- data_buf_lo_scan_elements()

  Return -EIO after releasing the just acquired bh_log buffer.

- ea_alloc_blk()
- ea_write()
- ea_set_block()

  Return -EIO. The function can currently return errors.

Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
---
 fs/gfs2/bmap.c  | 25 +++++++++++++++++--------
 fs/gfs2/dir.c   |  2 ++
 fs/gfs2/inode.c | 20 +++++++++++++++-----
 fs/gfs2/lops.c  |  8 ++++++++
 fs/gfs2/xattr.c |  6 ++++++
 5 files changed, 48 insertions(+), 13 deletions(-)

diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index a766b5816634d360a4bbae89f8bcdd284071864a..1c14313acea084584ae5de69a1107b45e470606a 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -608,9 +608,9 @@ static int gfs2_hole_size(struct inode *inode, sector_t lblock, u64 len,
 	return ret;
 }
 
-static inline void gfs2_indirect_init(struct metapath *mp,
-				      struct gfs2_glock *gl, unsigned int i,
-				      unsigned offset, u64 bn)
+static inline int gfs2_indirect_init(struct metapath *mp,
+				     struct gfs2_glock *gl, unsigned int i,
+				     unsigned int offset, u64 bn)
 {
 	__be64 *ptr = (__be64 *)(mp->mp_bh[i - 1]->b_data +
 		       ((i > 1) ? sizeof(struct gfs2_meta_header) :
@@ -618,11 +618,14 @@ static inline void gfs2_indirect_init(struct metapath *mp,
 	BUG_ON(i < 1);
 	BUG_ON(mp->mp_bh[i] != NULL);
 	mp->mp_bh[i] = gfs2_meta_new(gl, bn);
+	if (!mp->mp_bh[i])
+		return -EIO;
 	gfs2_trans_add_meta(gl, mp->mp_bh[i]);
 	gfs2_metatype_set(mp->mp_bh[i], GFS2_METATYPE_IN, GFS2_FORMAT_IN);
 	gfs2_buffer_clear_tail(mp->mp_bh[i], sizeof(struct gfs2_meta_header));
 	ptr += offset;
 	*ptr = cpu_to_be64(bn);
+	return 0;
 }
 
 enum alloc_state {
@@ -723,8 +726,11 @@ static int __gfs2_iomap_alloc(struct inode *inode, struct iomap *iomap,
 				zero_bn = *ptr;
 			}
 			for (; i - 1 < mp->mp_fheight - ip->i_height && n > 0;
-			     i++, n--)
-				gfs2_indirect_init(mp, ip->i_gl, i, 0, bn++);
+			     i++, n--) {
+				ret = gfs2_indirect_init(mp, ip->i_gl, i, 0, bn++);
+				if (ret)
+					goto out;
+			}
 			if (i - 1 == mp->mp_fheight - ip->i_height) {
 				i--;
 				gfs2_buffer_copy_tail(mp->mp_bh[i],
@@ -751,9 +757,12 @@ static int __gfs2_iomap_alloc(struct inode *inode, struct iomap *iomap,
 		case ALLOC_GROW_DEPTH:
 			if (i > 1 && i < mp->mp_fheight)
 				gfs2_trans_add_meta(ip->i_gl, mp->mp_bh[i-1]);
-			for (; i < mp->mp_fheight && n > 0; i++, n--)
-				gfs2_indirect_init(mp, ip->i_gl, i,
-						   mp->mp_list[i-1], bn++);
+			for (; i < mp->mp_fheight && n > 0; i++, n--) {
+				ret = gfs2_indirect_init(mp, ip->i_gl, i,
+							 mp->mp_list[i-1], bn++);
+				if (ret)
+					goto out;
+			}
 			if (i == mp->mp_fheight)
 				state = ALLOC_DATA;
 			if (n == 0)
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 0d92f6c33dcb85dfa2a4b07f9f09575834e75e01..d1dc1e94b6c432d2ede504b04cb71959285a945d 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -93,6 +93,8 @@ int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block,
 	struct buffer_head *bh;
 
 	bh = gfs2_meta_new(ip->i_gl, block);
+	if (!bh)
+		return -EIO;
 	gfs2_trans_add_meta(ip->i_gl, bh);
 	gfs2_metatype_set(bh, GFS2_METATYPE_JD, GFS2_FORMAT_JD);
 	gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header));
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index f361876c5583353a1196ed35be1c95af33a90c02..dda51390fee49b56279161fdd755b3c2b6e7eac6 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -544,13 +544,15 @@ static void gfs2_init_dir(struct buffer_head *dibh,
  * take any ACLs, LSM xattrs, etc.
  */
 
-static void gfs2_init_xattr(struct gfs2_inode *ip)
+static int gfs2_init_xattr(struct gfs2_inode *ip)
 {
 	struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
 	struct buffer_head *bh;
 	struct gfs2_ea_header *ea;
 
 	bh = gfs2_meta_new(ip->i_gl, ip->i_eattr);
+	if (!bh)
+		return -EIO;
 	gfs2_trans_add_meta(ip->i_gl, bh);
 	gfs2_metatype_set(bh, GFS2_METATYPE_EA, GFS2_FORMAT_EA);
 	gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header));
@@ -561,6 +563,7 @@ static void gfs2_init_xattr(struct gfs2_inode *ip)
 	ea->ea_flags = GFS2_EAFLAG_LAST;
 
 	brelse(bh);
+	return 0;
 }
 
 /**
@@ -571,13 +574,15 @@ static void gfs2_init_xattr(struct gfs2_inode *ip)
  *
  */
 
-static void init_dinode(struct gfs2_inode *dip, struct gfs2_inode *ip,
-			const char *symname)
+static int init_dinode(struct gfs2_inode *dip, struct gfs2_inode *ip,
+		       const char *symname)
 {
 	struct gfs2_dinode *di;
 	struct buffer_head *dibh;
 
 	dibh = gfs2_meta_new(ip->i_gl, ip->i_no_addr);
+	if (!dibh)
+		return -EIO;
 	gfs2_trans_add_meta(ip->i_gl, dibh);
 	di = (struct gfs2_dinode *)dibh->b_data;
 	gfs2_dinode_out(ip, di);
@@ -602,6 +607,7 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_inode *ip,
 
 	set_buffer_uptodate(dibh);
 	brelse(dibh);
+	return 0;
 }
 
 /**
@@ -864,10 +870,14 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
 		goto fail_gunlock3;
 
 	if (blocks > 1) {
-		gfs2_init_xattr(ip);
+		error = gfs2_init_xattr(ip);
+		if (error)
+			goto fail_gunlock3;
 		xattr_initialized = true;
 	}
-	init_dinode(dip, ip, symname);
+	error = init_dinode(dip, ip, symname);
+	if (error)
+		goto fail_gunlock3;
 	gfs2_trans_end(sdp);
 
 	glock_set_object(ip->i_gl, ip);
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 6dabe73ad790d9cbf6f2725dd3e5e1c40c3781b8..192e71e630dce062361cc754f2ba878fa23c0a7f 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -801,6 +801,10 @@ static int buf_lo_scan_elements(struct gfs2_jdesc *jd, u32 start,
 			return error;
 
 		bh_ip = gfs2_meta_new(gl, blkno);
+		if (!bh_ip) {
+			brelse(bh_log);
+			return -EIO;
+		}
 		memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size);
 
 		if (gfs2_meta_check(sdp, bh_ip))
@@ -1026,6 +1030,10 @@ static int databuf_lo_scan_elements(struct gfs2_jdesc *jd, u32 start,
 			return error;
 
 		bh_ip = gfs2_meta_new(gl, blkno);
+		if (!bh_ip) {
+			brelse(bh_log);
+			return -EIO;
+		}
 		memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size);
 
 		/* Unescape */
diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c
index b9f48d6f10a97a36365a2fe636ceb1cd476e4318..5aec4d0f240dc5beef18bc5619faf6b375152c03 100644
--- a/fs/gfs2/xattr.c
+++ b/fs/gfs2/xattr.c
@@ -648,6 +648,8 @@ static int ea_alloc_blk(struct gfs2_inode *ip, struct buffer_head **bhp)
 		return error;
 	gfs2_trans_remove_revoke(sdp, block, 1);
 	*bhp = gfs2_meta_new(ip->i_gl, block);
+	if (!*bhp)
+		return -EIO;
 	gfs2_trans_add_meta(ip->i_gl, *bhp);
 	gfs2_metatype_set(*bhp, GFS2_METATYPE_EA, GFS2_FORMAT_EA);
 	gfs2_buffer_clear_tail(*bhp, sizeof(struct gfs2_meta_header));
@@ -710,6 +712,8 @@ static int ea_write(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
 				return error;
 			gfs2_trans_remove_revoke(sdp, block, 1);
 			bh = gfs2_meta_new(ip->i_gl, block);
+			if (!bh)
+				return -EIO;
 			gfs2_trans_add_meta(ip->i_gl, bh);
 			gfs2_metatype_set(bh, GFS2_METATYPE_ED, GFS2_FORMAT_ED);
 
@@ -1011,6 +1015,8 @@ static int ea_set_block(struct gfs2_inode *ip, struct gfs2_ea_request *er,
 			return error;
 		gfs2_trans_remove_revoke(sdp, blk, 1);
 		indbh = gfs2_meta_new(ip->i_gl, blk);
+		if (!indbh)
+			return -EIO;
 		gfs2_trans_add_meta(ip->i_gl, indbh);
 		gfs2_metatype_set(indbh, GFS2_METATYPE_IN, GFS2_FORMAT_IN);
 		gfs2_buffer_clear_tail(indbh, mh_size);

-- 
2.47.3


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 4/5] gfs2: handle NULL return value in callers of gfs2_meta_ra()
  2026-09-08 21:16 [PATCH 0/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() and its caller chain Mauricio Faria de Oliveira
                   ` (2 preceding siblings ...)
  2026-09-08 21:16 ` [PATCH 3/5] gfs2: handle NULL return value in callers of gfs2_meta_new() Mauricio Faria de Oliveira
@ 2026-09-08 21:16 ` Mauricio Faria de Oliveira
  2026-09-08 21:16 ` [PATCH 5/5] gfs2: check for errors from filemap_get_folio() in gfs2_jhead_process_page() Mauricio Faria de Oliveira
  4 siblings, 0 replies; 6+ messages in thread
From: Mauricio Faria de Oliveira @ 2026-09-08 21:16 UTC (permalink / raw)
  To: Andreas Gruenbacher
  Cc: kernel-dev, gfs2, linux-kernel, Mauricio Faria de Oliveira

Now that gfs2_meta_ra() can return NULL (instead of hit a kernel oops),
update its callers to handle it (and not hit another kernel oops).

If gfs2_meta_ra() returns NULL:

- gfs2_quota_init()

  It currently checks for NULL and returns -EIO.

- gfs2_dir_read_data()

  Return -EIO. Fail as currently done in other cases.

- gfs2_replay_read_block()

  Return -EIO. It can currently return errors.

Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
---
 fs/gfs2/dir.c      | 4 ++++
 fs/gfs2/recovery.c | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index d1dc1e94b6c432d2ede504b04cb71959285a945d..d9e0b6b19567c8aa8518b9245e9bdfd3ccf7c1a7 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -302,6 +302,10 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, __be64 *buf,
 				goto fail;
 			BUG_ON(extlen < 1);
 			bh = gfs2_meta_ra(ip->i_gl, dblock, extlen);
+			if (!bh) {
+				error = -EIO;
+				goto fail;
+			}
 		} else {
 			error = gfs2_meta_read(ip->i_gl, dblock, DIO_WAIT, 0, &bh);
 			if (error)
diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c
index 616c46aa3434ae6ad84d10d678ca3f6dbe9492f5..e39754e08f44d67b460a47744f4276cd168e356c 100644
--- a/fs/gfs2/recovery.c
+++ b/fs/gfs2/recovery.c
@@ -48,6 +48,8 @@ int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk,
 	}
 
 	*bh = gfs2_meta_ra(gl, dblock, extlen);
+	if (!*bh)
+		error = -EIO;
 
 	return error;
 }

-- 
2.47.3


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 5/5] gfs2: check for errors from filemap_get_folio() in gfs2_jhead_process_page()
  2026-09-08 21:16 [PATCH 0/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() and its caller chain Mauricio Faria de Oliveira
                   ` (3 preceding siblings ...)
  2026-09-08 21:16 ` [PATCH 4/5] gfs2: handle NULL return value in callers of gfs2_meta_ra() Mauricio Faria de Oliveira
@ 2026-09-08 21:16 ` Mauricio Faria de Oliveira
  4 siblings, 0 replies; 6+ messages in thread
From: Mauricio Faria de Oliveira @ 2026-09-08 21:16 UTC (permalink / raw)
  To: Andreas Gruenbacher
  Cc: kernel-dev, gfs2, linux-kernel, Mauricio Faria de Oliveira

Let's check for a not-found folio pointer error, that is, ERR_PTR(-ENOENT),
just in case, even though it is not expected (due to a previously acquired
reference on the folio, per the function comment).

If filemap_get_folio() returns an error, log it and return -EIO. The only
caller is gfs2_chain_bio() (same file), twice, which can currently return
errors, thus propagate the error.

Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
---
 fs/gfs2/lops.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 192e71e630dce062361cc754f2ba878fa23c0a7f..78cadde990182223978ddbde2be25cfe963beabe 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -459,14 +459,21 @@ static bool gfs2_jhead_folio_search(struct gfs2_jdesc *jd,
  * our references to the folio.
  */
 
-static void gfs2_jhead_process_page(struct gfs2_jdesc *jd, unsigned long index,
-				    struct gfs2_log_header_host *head,
-				    bool *done)
+static int gfs2_jhead_process_page(struct gfs2_jdesc *jd, unsigned long index,
+				   struct gfs2_log_header_host *head,
+				   bool *done)
 {
+	struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
 	struct folio *folio;
 
 	folio = filemap_get_folio(jd->jd_inode->i_mapping, index);
 
+	/* This should not happen (see comment above), but check just in case */
+	if (IS_ERR(folio)) {
+		fs_err(sdp, "Error %ld getting folio.\n", PTR_ERR(folio));
+		return -EIO;
+	}
+
 	folio_wait_locked(folio);
 	if (!folio_test_uptodate(folio))
 		*done = true;
@@ -476,6 +483,7 @@ static void gfs2_jhead_process_page(struct gfs2_jdesc *jd, unsigned long index,
 
 	/* filemap_get_folio() and the earlier filemap_grab_folio() */
 	folio_put_refs(folio, 2);
+	return 0;
 }
 
 static struct bio *gfs2_chain_bio(struct bio *prev, unsigned int nr_iovecs,
@@ -572,7 +580,9 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head)
 				continue;
 			}
 
-			gfs2_jhead_process_page(jd, blocks_read >> shift, head, &done);
+			ret = gfs2_jhead_process_page(jd, blocks_read >> shift, head, &done);
+			if (ret)
+				goto error;
 			blocks_read += PAGE_SIZE >> bsize_shift;
 			if (done)
 				goto out;  /* found */
@@ -583,7 +593,9 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head)
 	if (bio)
 		submit_bio(bio);
 	while (blocks_read < block) {
-		gfs2_jhead_process_page(jd, blocks_read >> shift, head, &done);
+		ret = gfs2_jhead_process_page(jd, blocks_read >> shift, head, &done);
+		if (ret)
+			goto error;
 		blocks_read += PAGE_SIZE >> bsize_shift;
 	}
 
@@ -592,6 +604,7 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head)
 
 	truncate_inode_pages(mapping, 0);
 
+error:
 	return ret;
 }
 

-- 
2.47.3


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-08 21:15 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-08 21:16 [PATCH 0/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() and its caller chain Mauricio Faria de Oliveira
2026-09-08 21:16 ` [PATCH 1/5] gfs2: handle errors from __filemap_get_folio() with FGP_CREAT in gfs2_getbuf() Mauricio Faria de Oliveira
2026-09-08 21:16 ` [PATCH 2/5] gfs2: handle NULL return value in callers of gfs2_getbuf() with CREATE Mauricio Faria de Oliveira
2026-09-08 21:16 ` [PATCH 3/5] gfs2: handle NULL return value in callers of gfs2_meta_new() Mauricio Faria de Oliveira
2026-09-08 21:16 ` [PATCH 4/5] gfs2: handle NULL return value in callers of gfs2_meta_ra() Mauricio Faria de Oliveira
2026-09-08 21:16 ` [PATCH 5/5] gfs2: check for errors from filemap_get_folio() in gfs2_jhead_process_page() Mauricio Faria de Oliveira

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®