view src/testdir/test_blob.vim @ 33811:06219b3bdaf3 v9.0.2121

patch 9.0.2121: [security]: use-after-free in ex_substitute Commit: https://github.com/vim/vim/commit/26c11c56888d01e298cd8044caf860f3c26f57bb Author: Christian Brabandt <cb@256bit.org> Date: Wed Nov 22 21:26:41 2023 +0100 patch 9.0.2121: [security]: use-after-free in ex_substitute Problem: [security]: use-after-free in ex_substitute Solution: always allocate memory closes: #13552 A recursive :substitute command could cause a heap-use-after free in Vim (CVE-2023-48706). The whole reproducible test is a bit tricky, I can only reproduce this reliably when no previous substitution command has been used yet (which is the reason, the test needs to run as first one in the test_substitute.vim file) and as a combination of the `:~` command together with a :s command that contains the special substitution atom `~\=` which will make use of a sub-replace special atom and calls a vim script function. There was a comment in the existing :s code, that already makes the `sub` variable allocate memory so that a recursive :s call won't be able to cause any issues here, so this was known as a potential problem already. But for the current test-case that one does not work, because the substitution does not start with `\=` but with `~\=` (and since there does not yet exist a previous substitution atom, Vim will simply increment the `sub` pointer (which then was not allocated dynamically) and later one happily use a sub-replace special expression (which could then free the `sub` var). The following commit fixes this, by making the sub var always using allocated memory, which also means we need to free the pointer whenever we leave the function. Since sub is now always an allocated variable, we also do no longer need the sub_copy variable anymore, since this one was used to indicated when sub pointed to allocated memory (and had therefore to be freed on exit) and when not. Github Security Advisory: https://github.com/vim/vim/security/advisories/GHSA-c8qm-x72m-q53q Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Wed, 22 Nov 2023 22:15:05 +0100
parents dbec60b8c253
children f2b94f240b7d
line wrap: on
line source

" Tests for the Blob types

import './vim9.vim' as v9

func TearDown()
  " Run garbage collection after every test
  call test_garbagecollect_now()
endfunc

" Tests for Blob type

" Blob creation from constant
func Test_blob_create()
  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call assert_equal(v:t_blob, type(b))
      call assert_equal(4, len(b))
      call assert_equal(0xDE, b[0])
      call assert_equal(0xAD, b[1])
      call assert_equal(0xBE, b[2])
      call assert_equal(0xEF, b[3])
      call assert_fails('VAR x = b[4]')

      call assert_equal(0xDE, get(b, 0))
      call assert_equal(0xEF, get(b, 3))

      call assert_fails('VAR b = 0z1', 'E973:')
      call assert_fails('VAR b = 0z1x', 'E973:')
      call assert_fails('VAR b = 0z12345', 'E973:')

      call assert_equal(0z, test_null_blob())

      LET b = 0z001122.33445566.778899.aabbcc.dd
      call assert_equal(0z00112233445566778899aabbccdd, b)
      call assert_fails('VAR b = 0z1.1')
      call assert_fails('VAR b = 0z.')
      call assert_fails('VAR b = 0z001122.')
      call assert_fails('call get("", 1)', 'E896:')
      call assert_equal(0, len(test_null_blob()))
      call assert_equal(0z, copy(test_null_blob()))
  END
  call v9.CheckLegacyAndVim9Success(lines)
endfunc

" assignment to a blob
func Test_blob_assign()
  let lines =<< trim END
      VAR b = 0zDEADBEEF
      VAR b2 = b[1 : 2]
      call assert_equal(0zADBE, b2)

      VAR bcopy = b[:]
      call assert_equal(b, bcopy)
      call assert_false(b is bcopy)

      LET b = 0zDEADBEEF
      LET b2 = b
      call assert_true(b is b2)
      LET b[:] = 0z11223344
      call assert_equal(0z11223344, b)
      call assert_equal(0z11223344, b2)
      call assert_true(b is b2)

      LET b = 0zDEADBEEF
      LET b[3 :] = 0z66
      call assert_equal(0zDEADBE66, b)
      LET b[: 1] = 0z8899
      call assert_equal(0z8899BE66, b)

      LET b = 0zDEADBEEF
      LET b += 0z99
      call assert_equal(0zDEADBEEF99, b)

      VAR l = [0z12]
      VAR m = deepcopy(l)
      LET m[0] = 0z34	#" E742 or E741 should not occur.
  END
  call v9.CheckLegacyAndVim9Success(lines)

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      LET b[2 : 3] = 0z112233
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E972:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      LET b[2 : 3] = 0z11
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E972:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      LET b[3 : 2] = 0z
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      LET b ..= 0z33
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E734:', 'E1019:', 'E734:'])

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      LET b ..= "xx"
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E734:', 'E1019:', 'E734:'])

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      LET b += "xx"
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E734:', 'E1012:', 'E734:'])

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      LET b[1 : 1] ..= 0z55
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E734:', 'E1183:', 'E734:'])

  call assert_fails('let b = readblob("a1b2c3")', 'E484:')
endfunc

func Test_blob_get_range()
  let lines =<< trim END
      VAR b = 0z0011223344
      call assert_equal(0z2233, b[2 : 3])
      call assert_equal(0z223344, b[2 : -1])
      call assert_equal(0z00, b[0 : -5])
      call assert_equal(0z, b[0 : -11])
      call assert_equal(0z44, b[-1 :])
      call assert_equal(0z0011223344, b[:])
      call assert_equal(0z0011223344, b[: -1])
      call assert_equal(0z, b[5 : 6])
      call assert_equal(0z0011, b[-10 : 1])
  END
  call v9.CheckLegacyAndVim9Success(lines)

  " legacy script white space
  let b = 0z0011223344
  call assert_equal(0z2233, b[2:3])
endfunc

func Test_blob_get()
  let lines =<< trim END
      VAR b = 0z0011223344
      call assert_equal(0x00, get(b, 0))
      call assert_equal(0x22, get(b, 2, 999))
      call assert_equal(0x44, get(b, 4))
      call assert_equal(0x44, get(b, -1))
      call assert_equal(-1, get(b, 5))
      call assert_equal(999, get(b, 5, 999))
      call assert_equal(-1, get(b, -8))
      call assert_equal(999, get(b, -8, 999))
      call assert_equal(10, get(test_null_blob(), 2, 10))

      call assert_equal(0x00, b[0])
      call assert_equal(0x22, b[2])
      call assert_equal(0x44, b[4])
      call assert_equal(0x44, b[-1])
  END
  call v9.CheckLegacyAndVim9Success(lines)

  let lines =<< trim END
      VAR b = 0z0011223344
      echo b[5]
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')

  let lines =<< trim END
      VAR b = 0z0011223344
      echo b[-8]
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')
endfunc

func Test_blob_to_string()
  let lines =<< trim END
      VAR b = 0z00112233445566778899aabbccdd
      call assert_equal('0z00112233.44556677.8899AABB.CCDD', string(b))
      call assert_equal(b, eval(string(b)))
      call remove(b, 4, -1)
      call assert_equal('0z00112233', string(b))
      call remove(b, 0, 3)
      call assert_equal('0z', string(b))
      call assert_equal('0z', string(test_null_blob()))
  END
  call v9.CheckLegacyAndVim9Success(lines)
endfunc

func Test_blob_compare()
  let lines =<< trim END
      VAR b1 = 0z0011
      VAR b2 = 0z1100
      VAR b3 = 0z001122
      call assert_true(b1 == b1)
      call assert_false(b1 == b2)
      call assert_false(b1 == b3)
      call assert_true(b1 != b2)
      call assert_true(b1 != b3)
      call assert_true(b1 == 0z0011)

      call assert_false(b1 is b2)
      LET b2 = b1
      call assert_true(b1 == b2)
      call assert_true(b1 is b2)
      LET b2 = copy(b1)
      call assert_true(b1 == b2)
      call assert_false(b1 is b2)
      LET b2 = b1[:]
      call assert_true(b1 == b2)
      call assert_false(b1 is b2)
      call assert_true(b1 isnot b2)
      call assert_true(0z != 0z10)
      call assert_true(0z10 != 0z)
  END
  call v9.CheckLegacyAndVim9Success(lines)

  let lines =<< trim END
      VAR b1 = 0z0011
      echo b1 == 9
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E977:', 'E1072', 'E1072'])

  let lines =<< trim END
      VAR b1 = 0z0011
      echo b1 != 9
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E977:', 'E1072', 'E1072'])

  let lines =<< trim END
      VAR b1 = 0z0011
      VAR b2 = 0z1100
      VAR x = b1 > b2
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E978:', 'E1072:', 'E1072:'])

  let lines =<< trim END
      VAR b1 = 0z0011
      VAR b2 = 0z1100
      VAR x = b1 < b2
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E978:', 'E1072:', 'E1072:'])

  let lines =<< trim END
      VAR b1 = 0z0011
      VAR b2 = 0z1100
      VAR x = b1 - b2
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E974:', 'E1036:', 'E974:'])

  let lines =<< trim END
      VAR b1 = 0z0011
      VAR b2 = 0z1100
      VAR x = b1 / b2
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E974:', 'E1036:', 'E974:'])

  let lines =<< trim END
      VAR b1 = 0z0011
      VAR b2 = 0z1100
      VAR x = b1 * b2
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E974:', 'E1036:', 'E974:'])
endfunc

func Test_blob_index_assign()
  let lines =<< trim END
      VAR b = 0z00
      LET b[1] = 0x11
      LET b[2] = 0x22
      LET b[0] = 0x33
      call assert_equal(0z331122, b)
  END
  call v9.CheckLegacyAndVim9Success(lines)

  let lines =<< trim END
      VAR b = 0z00
      LET b[2] = 0x33
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')

  let lines =<< trim END
      VAR b = 0z00
      LET b[-2] = 0x33
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')

  let lines =<< trim END
      VAR b = 0z00010203
      LET b[0 : -1] = 0z33
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')

  let lines =<< trim END
      VAR b = 0z00010203
      LET b[3 : 4] = 0z3344
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')
endfunc

func Test_blob_for_loop()
  let lines =<< trim END
      VAR blob = 0z00010203
      VAR i = 0
      for byte in blob
        call assert_equal(i, byte)
        LET i += 1
      endfor
      call assert_equal(4, i)

      LET blob = 0z00
      call remove(blob, 0)
      call assert_equal(0, len(blob))
      for byte in blob
        call assert_report('loop over empty blob')
      endfor

      LET blob = 0z0001020304
      LET i = 0
      for byte in blob
        call assert_equal(i, byte)
        if i == 1
          call remove(blob, 0)
        elseif i == 3
          call remove(blob, 3)
        endif
        LET i += 1
      endfor
      call assert_equal(5, i)
  END
  call v9.CheckLegacyAndVim9Success(lines)
endfunc

func Test_blob_concatenate()
  let lines =<< trim END
      VAR b = 0z0011
      LET b += 0z2233
      call assert_equal(0z00112233, b)

      LET b = 0zDEAD + 0zBEEF
      call assert_equal(0zDEADBEEF, b)
  END
  call v9.CheckLegacyAndVim9Success(lines)

  let lines =<< trim END
      VAR b = 0z0011
      LET b += "a"
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E734:', 'E1012:', 'E734:'])

  let lines =<< trim END
      VAR b = 0z0011
      LET b += 88
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E734:', 'E1012:', 'E734:'])
endfunc

func Test_blob_add()
  let lines =<< trim END
      VAR b = 0z0011
      call add(b, 0x22)
      call assert_equal(0z001122, b)
  END
  call v9.CheckLegacyAndVim9Success(lines)

  " Only works in legacy script
  let b = 0z0011
  call add(b, '51')
  call assert_equal(0z001133, b)
  call assert_equal(1, add(test_null_blob(), 0x22))

  let lines =<< trim END
      VAR b = 0z0011
      call add(b, [9])
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E745:', 'E1012:', 'E1210:'])

  let lines =<< trim END
      VAR b = 0z0011
      call add("", 0x01)
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E897:', 'E1013:', 'E1226:'])

  let lines =<< trim END
      add(test_null_blob(), 0x22)
  END
  call v9.CheckDefExecAndScriptFailure(lines, 'E1131:')

  let lines =<< trim END
      let b = 0zDEADBEEF
      lockvar b
      call add(b, 0)
      unlockvar b
  END
  call v9.CheckScriptFailure(lines, 'E741:')
endfunc

func Test_blob_empty()
  call assert_false(empty(0z001122))
  call assert_true(empty(0z))
  call assert_true(empty(test_null_blob()))
endfunc

" Test removing items in blob
func Test_blob_func_remove()
  let lines =<< trim END
      #" Test removing 1 element
      VAR b = 0zDEADBEEF
      call assert_equal(0xDE, remove(b, 0))
      call assert_equal(0zADBEEF, b)

      LET b = 0zDEADBEEF
      call assert_equal(0xEF, remove(b, -1))
      call assert_equal(0zDEADBE, b)

      LET b = 0zDEADBEEF
      call assert_equal(0xAD, remove(b, 1))
      call assert_equal(0zDEBEEF, b)

      #" Test removing range of element(s)
      LET b = 0zDEADBEEF
      call assert_equal(0zBE, remove(b, 2, 2))
      call assert_equal(0zDEADEF, b)

      LET b = 0zDEADBEEF
      call assert_equal(0zADBE, remove(b, 1, 2))
      call assert_equal(0zDEEF, b)
  END
  call v9.CheckLegacyAndVim9Success(lines)

  " Test invalid cases
  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call remove(b, 5)
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call remove(b, 1, 5)
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call remove(b, -10)
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call remove(b, 3, 2)
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call remove(test_null_blob(), 1, 2)
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E979:')

  let lines =<< trim END
      let b = 0zDEADBEEF
      lockvar b
      call remove(b, 0)
      unlockvar b
  END
  call v9.CheckScriptFailure(lines, 'E741:')

  " can only check at script level, not in a :def function
  let lines =<< trim END
      vim9script
      var b = 0zDEADBEEF
      lockvar b
      remove(b, 0)
  END
  call v9.CheckScriptFailure(lines, 'E741:')

  call assert_fails('echo remove(0z1020, [])', 'E745:')
  call assert_fails('echo remove(0z1020, 0, [])', 'E745:')
endfunc

func Test_blob_read_write()
  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call writefile(b, 'Xblob')
      VAR br = readfile('Xblob', 'B')
      call assert_equal(b, br)
      VAR br2 = readblob('Xblob')
      call assert_equal(b, br2)
      VAR br3 = readblob('Xblob', 1)
      call assert_equal(b[1 :], br3)
      VAR br4 = readblob('Xblob', 1, 2)
      call assert_equal(b[1 : 2], br4)
      VAR br5 = readblob('Xblob', -3)
      call assert_equal(b[-3 :], br5)
      VAR br6 = readblob('Xblob', -3, 2)
      call assert_equal(b[-3 : -2], br6)

      #" reading past end of file, empty result
      VAR br1e = readblob('Xblob', 10000)
      call assert_equal(0z, br1e)

      #" reading too much, result is truncated
      VAR blong = readblob('Xblob', -1000)
      call assert_equal(b, blong)
      LET blong = readblob('Xblob', -10, 8)
      call assert_equal(b, blong)
      LET blong = readblob('Xblob', 0, 10)
      call assert_equal(b, blong)

      call delete('Xblob')
  END
  call v9.CheckLegacyAndVim9Success(lines)

  if filereadable('/dev/random')
    let b = readblob('/dev/random', 0, 10)
    call assert_equal(10, len(b))
  endif

  call assert_fails("call readblob('notexist')", 'E484:')
  " TODO: How do we test for the E485 error?

  " This was crashing when calling readfile() with a directory.
  call assert_fails("call readfile('.', 'B')", 'E17: "." is a directory')
endfunc

" filter() item in blob
func Test_blob_filter()
  let lines =<< trim END
      call assert_equal(test_null_blob(), filter(test_null_blob(), '0'))
      call assert_equal(0z, filter(0zDEADBEEF, '0'))
      call assert_equal(0zADBEEF, filter(0zDEADBEEF, 'v:val != 0xDE'))
      call assert_equal(0zDEADEF, filter(0zDEADBEEF, 'v:val != 0xBE'))
      call assert_equal(0zDEADBE, filter(0zDEADBEEF, 'v:val != 0xEF'))
      call assert_equal(0zDEADBEEF, filter(0zDEADBEEF, '1'))
      call assert_equal(0z01030103, filter(0z010203010203, 'v:val != 0x02'))
      call assert_equal(0zADEF, filter(0zDEADBEEF, 'v:key % 2'))
  END
  call v9.CheckLegacyAndVim9Success(lines)
  call assert_fails('echo filter(0z10, "a10")', 'E121:')
endfunc

" map() item in blob
func Test_blob_map()
  let lines =<< trim END
      call assert_equal(0zDFAEBFF0, map(0zDEADBEEF, 'v:val + 1'))
      call assert_equal(0z00010203, map(0zDEADBEEF, 'v:key'))
      call assert_equal(0zDEAEC0F2, map(0zDEADBEEF, 'v:key + v:val'))
  END
  call v9.CheckLegacyAndVim9Success(lines)

  let lines =<< trim END
      call map(0z00, '[9]')
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E978:')
  call assert_fails('echo map(0z10, "a10")', 'E121:')
endfunc

func Test_blob_index()
  let lines =<< trim END
      call assert_equal(2, index(0zDEADBEEF, 0xBE))
      call assert_equal(-1, index(0zDEADBEEF, 0))
      call assert_equal(2, index(0z11111111, 0x11, 2))
      call assert_equal(3, 0z11110111->index(0x11, 2))
      call assert_equal(2, index(0z11111111, 0x11, -2))
      call assert_equal(3, index(0z11110111, 0x11, -2))
      call assert_equal(0, index(0z11110111, 0x11, -10))
      call assert_equal(-1, index(test_null_blob(), 1))
  END
  call v9.CheckLegacyAndVim9Success(lines)
endfunc

func Test_blob_insert()
  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call insert(b, 0x33)
      call assert_equal(0z33DEADBEEF, b)

      LET b = 0zDEADBEEF
      call insert(b, 0x33, 2)
      call assert_equal(0zDEAD33BEEF, b)
  END
  call v9.CheckLegacyAndVim9Success(lines)

  " only works in legacy script
  call assert_equal(0, insert(test_null_blob(), 0x33))

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call insert(b, -1)
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E475:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call insert(b, 257)
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E475:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call insert(b, 0, [9])
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E745:', 'E1013:', 'E1210:'])

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call insert(b, 0, -20)
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E475:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call insert(b, 0, 20)
  END
  call v9.CheckLegacyAndVim9Failure(lines, 'E475:')

  let lines =<< trim END
      VAR b = 0zDEADBEEF
      call insert(b, [])
  END
  call v9.CheckLegacyAndVim9Failure(lines, ['E745:', 'E1013:', 'E1210:'])

  let lines =<< trim END
      insert(test_null_blob(), 0x33)
  END
  call v9.CheckDefExecAndScriptFailure(lines, 'E1131:')

  let lines =<< trim END
      let b = 0zDEADBEEF
      lockvar b
      call insert(b, 3)
      unlockvar b
  END
  call v9.CheckScriptFailure(lines, 'E741:')

  let lines =<< trim END
      vim9script
      var b = 0zDEADBEEF
      lockvar b
      insert(b, 3)
  END
  call v9.CheckScriptFailure(lines, 'E741:')
endfunc

func Test_blob_reverse()
  let lines =<< trim END
      call assert_equal(0zEFBEADDE, reverse(0zDEADBEEF))
      call assert_equal(0zBEADDE, reverse(0zDEADBE))
      call assert_equal(0zADDE, reverse(0zDEAD))
      call assert_equal(0zDE, reverse(0zDE))
      call assert_equal(0z, reverse(test_null_blob()))
  END
  call v9.CheckLegacyAndVim9Success(lines)
endfunc

func Test_blob_json_encode()
  let lines =<< trim END
      call assert_equal('[222,173,190,239]', json_encode(0zDEADBEEF))
      call assert_equal('[]', json_encode(0z))
  END
  call v9.CheckLegacyAndVim9Success(lines)
endfunc

func Test_blob_lock()
  let lines =<< trim END
      let b = 0z112233
      lockvar b
      unlockvar b
      let b = 0z44
  END
  call v9.CheckScriptSuccess(lines)

  let lines =<< trim END
      vim9script
      var b = 0z112233
      lockvar b
      unlockvar b
      b = 0z44
  END
  call v9.CheckScriptSuccess(lines)

  let lines =<< trim END
      let b = 0z112233
      lockvar b
      let b = 0z44
  END
  call v9.CheckScriptFailure(lines, 'E741:')

  let lines =<< trim END
      vim9script
      var b = 0z112233
      lockvar b
      b = 0z44
  END
  call v9.CheckScriptFailure(lines, 'E741:')
endfunc

func Test_blob_sort()
  call v9.CheckLegacyAndVim9Failure(['call sort([1.0, 0z11], "f")'], 'E975:')
  call v9.CheckLegacyAndVim9Failure(['call sort([11, 0z11], "N")'], 'E974:')
endfunc

" Tests for the blob2list() function
func Test_blob2list()
  call assert_fails('let v = blob2list(10)', 'E1238: Blob required for argument 1')
  eval 0zFFFF->blob2list()->assert_equal([255, 255])
  let tests = [[0z0102, [1, 2]],
        \ [0z00, [0]],
        \ [0z, []],
        \ [0z00000000, [0, 0, 0, 0]],
        \ [0zAABB.CCDD, [170, 187, 204, 221]]]
  for t in tests
    call assert_equal(t[0]->blob2list(), t[1])
  endfor
  exe 'let v = 0z' .. repeat('000102030405060708090A0B0C0D0E0F', 64)
  call assert_equal(1024, blob2list(v)->len())
  call assert_equal([4, 8, 15], [v[100], v[1000], v[1023]])
  call assert_equal([], blob2list(test_null_blob()))
endfunc

" Tests for the list2blob() function
func Test_list2blob()
  call assert_fails('let b = list2blob(0z10)', 'E1211: List required for argument 1')
  let tests = [[[1, 2], 0z0102],
        \ [[0], 0z00],
        \ [[], 0z],
        \ [[0, 0, 0, 0], 0z00000000],
        \ [[255, 255], 0zFFFF],
        \ [[170, 187, 204, 221], 0zAABB.CCDD],
        \ ]
  for t in tests
    call assert_equal(t[1], t[0]->list2blob())
  endfor
  call assert_fails('let b = list2blob([1, []])', 'E745:')
  call assert_fails('let b = list2blob([-1])', 'E1239:')
  call assert_fails('let b = list2blob([256])', 'E1239:')
  let b = range(16)->repeat(64)->list2blob()
  call assert_equal(1024, b->len())
  call assert_equal([4, 8, 15], [b[100], b[1000], b[1023]])

  call assert_equal(0z, list2blob(test_null_list()))
  call assert_equal(0z00010203, list2blob(range(4)))
endfunc

" The following used to cause an out-of-bounds memory access
func Test_blob2string()
  let v = '0z' .. repeat('01010101.', 444)
  let v ..= '01'
  exe 'let b = ' .. v
  call assert_equal(v, string(b))
endfunc

func Test_blob_repeat()
  call assert_equal(0z, repeat(0z00, 0))
  call assert_equal(0z00, repeat(0z00, 1))
  call assert_equal(0z0000, repeat(0z00, 2))
  call assert_equal(0z00000000, repeat(0z0000, 2))

  call assert_equal(0z, repeat(0z12, 0))
  call assert_equal(0z, repeat(0z1234, 0))
  call assert_equal(0z1234, repeat(0z1234, 1))
  call assert_equal(0z12341234, repeat(0z1234, 2))
endfunc

" Test for blob allocation failure
func Test_blob_alloc_failure()
  " blob variable
  call test_alloc_fail(GetAllocId('blob_alloc'), 0, 0)
  call assert_fails('let v = 0z10', 'E342:')

  " blob slice
  let v = 0z1020
  call test_alloc_fail(GetAllocId('blob_alloc'), 0, 0)
  call assert_fails('let x = v[0:0]', 'E342:')
  call assert_equal(0z1020, x)

  " blob remove()
  let v = 0z10203040
  call test_alloc_fail(GetAllocId('blob_alloc'), 0, 0)
  call assert_fails('let x = remove(v, 1, 2)', 'E342:')
  call assert_equal(0, x)

  " list2blob()
  call test_alloc_fail(GetAllocId('blob_alloc'), 0, 0)
  call assert_fails('let a = list2blob([1, 2, 4])', 'E342:')
  call assert_equal(0, a)

  " mapnew()
  call test_alloc_fail(GetAllocId('blob_alloc'), 0, 0)
  call assert_fails('let x = mapnew(0z1234, {_, v -> 1})', 'E342:')
  call assert_equal(0, x)

  " copy()
  call test_alloc_fail(GetAllocId('blob_alloc'), 0, 0)
  call assert_fails('let x = copy(v)', 'E342:')
  call assert_equal(0z, x)

  " readblob()
  call test_alloc_fail(GetAllocId('blob_alloc'), 0, 0)
  call assert_fails('let x = readblob("test_blob.vim")', 'E342:')
  call assert_equal(0, x)
endfunc

" Test for the indexof() function
func Test_indexof()
  let b = 0zdeadbeef
  call assert_equal(0, indexof(b, {i, v -> v == 0xde}))
  call assert_equal(3, indexof(b, {i, v -> v == 0xef}))
  call assert_equal(-1, indexof(b, {i, v -> v == 0x1}))
  call assert_equal(1, indexof(b, "v:val == 0xad"))
  call assert_equal(-1, indexof(b, "v:val == 0xff"))
  call assert_equal(-1, indexof(b, {_, v -> "v == 0xad"}))

  call assert_equal(-1, indexof(0z, "v:val == 0x0"))
  call assert_equal(-1, indexof(test_null_blob(), "v:val == 0xde"))
  call assert_equal(-1, indexof(b, test_null_string()))
  call assert_equal(-1, indexof(b, test_null_function()))

  let b = 0z01020102
  call assert_equal(1, indexof(b, "v:val == 0x02", #{startidx: 0}))
  call assert_equal(2, indexof(b, "v:val == 0x01", #{startidx: -2}))
  call assert_equal(-1, indexof(b, "v:val == 0x01", #{startidx: 5}))
  call assert_equal(0, indexof(b, "v:val == 0x01", #{startidx: -5}))
  call assert_equal(0, indexof(b, "v:val == 0x01", test_null_dict()))

  " failure cases
  call assert_fails('let i = indexof(b, "val == 0xde")', 'E121:')
  call assert_fails('let i = indexof(b, {})', 'E1256:')
endfunc

" vim: shiftwidth=2 sts=2 expandtab