Re: [bug report] smb3: retrying on failed server close

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This should be the simpler way to fix this, which matches the unwind ladder.   I moved the goto label to the
correct place so the frees are in the right order and switched the two gotos which were backwards.

Let me know if any objections or additional cleanup you think is important.

On Fri, Jul 19, 2024 at 5:05 PM Dan Carpenter <dan.carpenter@xxxxxxxxxx> wrote:
Hello Ritvik Budhiraja,

Commit 173217bd7336 ("smb3: retrying on failed server close") from
Apr 2, 2024 (linux-next), leads to the following Smatch static
checker warning:

        fs/smb/client/cifsfs.c:1981 init_cifs()
        error: we previously assumed 'serverclose_wq' could be null (see line 1895)

I've written a blog about best practices for how kernel unwind ladders are
normally written.

https://staticthinking.wordpress.com/2022/04/28/free-the-last-thing-style/

fs/smb/client/cifsfs.c
  1800  static int __init
  1801  init_cifs(void)
  1802  {
  1803          int rc = 0;
  1804          cifs_proc_init();

This creates proc files.

  1805          INIT_LIST_HEAD(&cifs_tcp_ses_list);
  1806  /*
  1807   *  Initialize Global counters
  1808   */
  1809          atomic_set(&sesInfoAllocCount, 0);
  1810          atomic_set(&tconInfoAllocCount, 0);
  1811          atomic_set(&tcpSesNextId, 0);
  1812          atomic_set(&tcpSesAllocCount, 0);
  1813          atomic_set(&tcpSesReconnectCount, 0);
  1814          atomic_set(&tconInfoReconnectCount, 0);
  1815 
  1816          atomic_set(&buf_alloc_count, 0);
  1817          atomic_set(&small_buf_alloc_count, 0);
  1818  #ifdef CONFIG_CIFS_STATS2
  1819          atomic_set(&total_buf_alloc_count, 0);
  1820          atomic_set(&total_small_buf_alloc_count, 0);
  1821          if (slow_rsp_threshold < 1)
  1822                  cifs_dbg(FYI, "slow_response_threshold msgs disabled\n");
  1823          else if (slow_rsp_threshold > 32767)
  1824                  cifs_dbg(VFS,
  1825                         "slow response threshold set higher than recommended (0 to 32767)\n");
  1826  #endif /* CONFIG_CIFS_STATS2 */
  1827 
  1828          atomic_set(&mid_count, 0);
  1829          GlobalCurrentXid = 0;
  1830          GlobalTotalActiveXid = 0;
  1831          GlobalMaxActiveXid = 0;
  1832          spin_lock_init(&cifs_tcp_ses_lock);
  1833          spin_lock_init(&GlobalMid_Lock);
  1834 
  1835          cifs_lock_secret = get_random_u32();
  1836 
  1837          if (cifs_max_pending < 2) {
  1838                  cifs_max_pending = 2;
  1839                  cifs_dbg(FYI, "cifs_max_pending set to min of 2\n");
  1840          } else if (cifs_max_pending > CIFS_MAX_REQ) {
  1841                  cifs_max_pending = CIFS_MAX_REQ;
  1842                  cifs_dbg(FYI, "cifs_max_pending set to max of %u\n",
  1843                           CIFS_MAX_REQ);
  1844          }
  1845 
  1846          /* Limit max to about 18 hours, and setting to zero disables directory entry caching */
  1847          if (dir_cache_timeout > 65000) {
  1848                  dir_cache_timeout = 65000;
  1849                  cifs_dbg(VFS, "dir_cache_timeout set to max of 65000 seconds\n");
  1850          }
  1851 
  1852          cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
  1853          if (!cifsiod_wq) {
  1854                  rc = -ENOMEM;
  1855                  goto out_clean_proc;

Generally I can tell from these gotos that they free the last resource.  This
one deletes the proc files.

  1856          }
  1857 
  1858          /*
  1859           * Consider in future setting limit!=0 maybe to min(num_of_cores - 1, 3)
  1860           * so that we don't launch too many worker threads but
  1861           * Documentation/core-api/workqueue.rst recommends setting it to 0
  1862           */
  1863 
  1864          /* WQ_UNBOUND allows decrypt tasks to run on any CPU */
  1865          decrypt_wq = alloc_workqueue("smb3decryptd",
  1866                                       WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
  1867          if (!decrypt_wq) {
  1868                  rc = -ENOMEM;
  1869                  goto out_destroy_cifsiod_wq;

cifsiod_wq alloc and destroyed.  Fine.

  1870          }
  1871 
  1872          fileinfo_put_wq = alloc_workqueue("cifsfileinfoput",
  1873                                       WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
  1874          if (!fileinfo_put_wq) {
  1875                  rc = -ENOMEM;
  1876                  goto out_destroy_decrypt_wq;
  1877          }
  1878 
  1879          cifsoplockd_wq = alloc_workqueue("cifsoplockd",
  1880                                           WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
  1881          if (!cifsoplockd_wq) {
  1882                  rc = -ENOMEM;
  1883                  goto out_destroy_fileinfo_put_wq;
  1884          }
  1885 
  1886          deferredclose_wq = alloc_workqueue("deferredclose",
  1887                                             WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
  1888          if (!deferredclose_wq) {
  1889                  rc = -ENOMEM;
  1890                  goto out_destroy_cifsoplockd_wq;
  1891          }
  1892 
  1893          serverclose_wq = alloc_workqueue("serverclose",
  1894                                             WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
  1895          if (!serverclose_wq) {
  1896                  rc = -ENOMEM;
  1897                  goto out_destroy_serverclose_wq;

This should be goto out_destroy_deferredclose_wq.

  1898          }
  1899 
  1900          rc = cifs_init_inodecache();
  1901          if (rc)
  1902                  goto out_destroy_deferredclose_wq;

This should be out_destroy_serverclose_wq.

  1903 
  1904          rc = cifs_init_netfs();
  1905          if (rc)
  1906                  goto out_destroy_inodecache;
  1907 
  1908          rc = init_mids();
  1909          if (rc)
  1910                  goto out_destroy_netfs;
  1911 
  1912          rc = cifs_init_request_bufs();
  1913          if (rc)
  1914                  goto out_destroy_mids;
  1915 
  1916  #ifdef CONFIG_CIFS_DFS_UPCALL
  1917          rc = dfs_cache_init();
  1918          if (rc)
  1919                  goto out_destroy_request_bufs;
  1920  #endif /* CONFIG_CIFS_DFS_UPCALL */
  1921  #ifdef CONFIG_CIFS_UPCALL
  1922          rc = init_cifs_spnego();
  1923          if (rc)
  1924                  goto out_destroy_dfs_cache;
  1925  #endif /* CONFIG_CIFS_UPCALL */
  1926  #ifdef CONFIG_CIFS_SWN_UPCALL
  1927          rc = cifs_genl_init();
  1928          if (rc)
  1929                  goto out_register_key_type;

This goto has a confusing name.  It calls exit_cifs_spnego().  It used to
unregister the spegno key type.  #HistoricalReasons

  1930  #endif /* CONFIG_CIFS_SWN_UPCALL */
  1931 
  1932          rc = init_cifs_idmap();
  1933          if (rc)
  1934                  goto out_cifs_swn_init;
  1935 
  1936          rc = register_filesystem(&cifs_fs_type);
  1937          if (rc)
  1938                  goto out_init_cifs_idmap;
  1939 
  1940          rc = register_filesystem(&smb3_fs_type);
  1941          if (rc) {
  1942                  unregister_filesystem(&cifs_fs_type);

Do this in the unwind ladder, not before the goto.

  1943                  goto out_init_cifs_idmap;
  1944          }
  1945 
  1946          return 0;
  1947 
  1948  out_init_cifs_idmap:
  1949          exit_cifs_idmap();
  1950  out_cifs_swn_init:
  1951  #ifdef CONFIG_CIFS_SWN_UPCALL
  1952          cifs_genl_exit();
  1953  out_register_key_type:
  1954  #endif
  1955  #ifdef CONFIG_CIFS_UPCALL
  1956          exit_cifs_spnego();
  1957  out_destroy_dfs_cache:
  1958  #endif
  1959  #ifdef CONFIG_CIFS_DFS_UPCALL
  1960          dfs_cache_destroy();
  1961  out_destroy_request_bufs:
  1962  #endif

These ifdef are a bit complicated.  It might be nicer to write it as:

out_cifs_swn_init:
        if (IS_ENABLED(CONFIG_CIFS_SWN_UPCALL))
                cifs_genl_exit();

  1963          cifs_destroy_request_bufs();
  1964  out_destroy_mids:
  1965          destroy_mids();
  1966  out_destroy_netfs:
  1967          cifs_destroy_netfs();
  1968  out_destroy_inodecache:
  1969          cifs_destroy_inodecache();
  1970  out_destroy_deferredclose_wq:
  1971          destroy_workqueue(deferredclose_wq);
  1972  out_destroy_cifsoplockd_wq:
  1973          destroy_workqueue(cifsoplockd_wq);
  1974  out_destroy_fileinfo_put_wq:
  1975          destroy_workqueue(fileinfo_put_wq);
  1976  out_destroy_decrypt_wq:
  1977          destroy_workqueue(decrypt_wq);
  1978  out_destroy_cifsiod_wq:
  1979          destroy_workqueue(cifsiod_wq);
  1980  out_destroy_serverclose_wq:
  1981          destroy_workqueue(serverclose_wq);
  1982  out_clean_proc:
  1983          cifs_proc_clean();

These need to be in mirror order from how they are allocated.  The last step is
to cut and paste the cleanup into exit_cifs(), add an
unregister_filesystem(&smb3_fs_type); and delete the labels.  There is an extra
step of cifs_release_automount_timer() which kills the timer.  I would have
expected that to be the first thing in the function, but maybe the ordering
doesn't matter or maybe I haven't understood how the ordering works.

  1984          return rc;
  1985  }

regards,
dan carpenter



--
Thanks,

Steve
From 8e2a1da3227a59fdd2ec6fc2dd347561b144327a Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@xxxxxxxxxxxxx>
Date: Sun, 21 Jul 2024 15:45:56 -0500
Subject: [PATCH] cifs: fix potential null pointer use in destroy_workqueue in
 init_cifs error path

Dan Carpenter reported a Smack static checker warning:
   fs/smb/client/cifsfs.c:1981 init_cifs()
   error: we previously assumed 'serverclose_wq' could be null (see line 1895)

The patch which introduced the serverclose workqueue used the wrong
oredering in error paths in init_cifs() for freeing it on errors.

Reported-by: Dan Carpenter <dan.carpenter@xxxxxxxxxx>
Fixes: 173217bd7336 ("smb3: retrying on failed server close")
Cc: stable@xxxxxxxxxxxxxxx
Cc: Ritvik Budhiraja <rbudhiraja@xxxxxxxxxxxxx>
Signed-off-by: Steve French <stfrench@xxxxxxxxxxxxx>
---
 fs/smb/client/cifsfs.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index c92937bed133..2c4b357d85e2 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -1894,12 +1894,12 @@ init_cifs(void)
 					   WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
 	if (!serverclose_wq) {
 		rc = -ENOMEM;
-		goto out_destroy_serverclose_wq;
+		goto out_destroy_deferredclose_wq;
 	}
 
 	rc = cifs_init_inodecache();
 	if (rc)
-		goto out_destroy_deferredclose_wq;
+		goto out_destroy_serverclose_wq;
 
 	rc = cifs_init_netfs();
 	if (rc)
@@ -1967,6 +1967,8 @@ init_cifs(void)
 	cifs_destroy_netfs();
 out_destroy_inodecache:
 	cifs_destroy_inodecache();
+out_destroy_serverclose_wq:
+	destroy_workqueue(serverclose_wq);
 out_destroy_deferredclose_wq:
 	destroy_workqueue(deferredclose_wq);
 out_destroy_cifsoplockd_wq:
@@ -1977,8 +1979,6 @@ init_cifs(void)
 	destroy_workqueue(decrypt_wq);
 out_destroy_cifsiod_wq:
 	destroy_workqueue(cifsiod_wq);
-out_destroy_serverclose_wq:
-	destroy_workqueue(serverclose_wq);
 out_clean_proc:
 	cifs_proc_clean();
 	return rc;
-- 
2.43.0


[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux