Repository: pwndbg/pwndbg

Issue:
ERROR :: Exception occurred: dq: Cannot convert value to long. (<class 'gdb.error'>)
<!--
Before reporting a new issue, make sure that we do not have any duplicates already open.
If there is one it might be good to take part in the discussion there.

Please make sure you have checked that the issue persists on LATEST pwndbg version.

Below is a template for BUG REPORTS.
Don't include it if this is a FEATURE REQUEST.
-->


### Description

ERROR: `Exception occurred: dq: Cannot convert value to long. (<class 'gdb.error'>)`
 i am getting this error while running the `dq` command
<!--
Briefly describe the problem you are having in a few paragraphs.
-->

### Steps to reproduce
i have a variable named target which has a string value "XXXXXX" and when i try to read its address using the dq command i get the error. 
<!--
What do we have to do to reproduce the problem?
If this is connected to particular C/asm code, 
please provide the smallest C code that reproduces the issue.
-->

Gdb session history:
vis
dq target 
dq target 
dq target
dump target
help dump
help dq
dump N qwords
help dump
xinfo target 

### My setup

<!--
Show us your gdb/python/pwndbg/OS/IDA Pro version (depending on your case).

NOTE: We are currently supporting only Ubuntu installations.
It is known that pwndbg is not fully working e.g. on Arch Linux (the heap stuff is not working there).
If you would like to change this situation - help us improving pwndbg and supporting other distros!

This can be displayed in pwndbg through `version` command.

If it is somehow unavailable, use:
* `show version` - for gdb
* `py import sys; print(sys.version)` - for python
* pwndbg version/git commit id
-->

Platform: Linux-5.11.0-22-generic-x86_64-with-glibc2.33
Gdb:      10.1.90.20210411-git
Python:   3.9.5 (default, May 11 2021, 08:20:37)  [GCC 10.3.0]
Pwndbg:   1.1.0 build: a79c85b
Capstone: 4.0.1024
Unicorn:  1.0.2
This GDB was configured as follows:
   configure --host=x86_64-linux-gnu --target=x86_64-linux-gnu
             --with-auto-load-dir=$debugdir:$datadir/auto-load
             --with-auto-load-safe-path=$debugdir:$datadir/auto-load
             --with-expat
             --with-gdb-datadir=/usr/share/gdb (relocatable)
             --with-jit-reader-dir=/usr/lib/gdb (relocatable)
             --without-libunwind-ia64
             --with-lzma
             --with-babeltrace
             --with-intel-pt
             --with-mpfr
             --with-xxhash
             --with-python=/usr (relocatable)
             --with-python-libdir=/usr/lib (relocatable)
             --with-debuginfod
             --without-guile
             --enable-source-highlight
             --with-separate-debug-dir=/usr/lib/debug (relocatable)
             --with-system-gdbinit=/etc/gdb/gdbinit
             --with-system-gdbinit-dir=/etc/gdb/gdbinit.d




Discussion:
The `dq` command expects an address, not a symbol. Use `dq &target`.

There was a discussion here how it should work: https://github.com/pwndbg/pwndbg/issues/788
Anyway, the error is not nice, I'll think of how to fix that.
okie thank you <#

Resolving patch:
diff --git a/pwndbg/commands/__init__.py b/pwndbg/commands/__init__.py
--- a/pwndbg/commands/__init__.py
+++ b/pwndbg/commands/__init__.py
@@ -321,6 +321,11 @@ def __call__(self, function):
             _ArgparsedCommand(self.parser, function, alias)
         return _ArgparsedCommand(self.parser, function)
 
+# We use a 64-bit max value literal here instead of pwndbg.arch.current
+# as realistically its ok to pull off the biggest possible type here
+# We cache its GDB value type which is 'unsigned long long'
+_mask = 0xffffffffFFFFFFFF
+_mask_val_type = gdb.Value(_mask).type
 
 def sloppy_gdb_parse(s):
     """
@@ -333,6 +338,13 @@ def sloppy_gdb_parse(s):
     :return: Whatever gdb.parse_and_eval returns or string.
     """
     try:
-        return gdb.parse_and_eval(s)
+        val = gdb.parse_and_eval(s)
+        # We can't just return int(val) because GDB may return:
+        # "Python Exception <class 'gdb.error'> Cannot convert value to long."
+        # e.g. for:
+        # pwndbg> pi int(gdb.parse_and_eval('__libc_start_main'))
+        #
+        # Here, the _mask_val.type should be `unsigned long long`
+        return int(val.cast(_mask_val_type))
     except (TypeError, gdb.error):
         return s
diff --git a/pwndbg/commands/windbg.py b/pwndbg/commands/windbg.py
--- a/pwndbg/commands/windbg.py
+++ b/pwndbg/commands/windbg.py
@@ -29,8 +29,8 @@ def get_type(size):
     }[size]
 
 parser = argparse.ArgumentParser(description="Starting at the specified address, dump N bytes.")
-parser.add_argument("address", type=int, help="The address to dump from.")
-parser.add_argument("count", type=int, default=64, nargs="?", help="The number of bytes to dump.")
+parser.add_argument("address", type=pwndbg.commands.sloppy_gdb_parse, help="The address to dump from.")
+parser.add_argument("count", type=pwndbg.commands.sloppy_gdb_parse, default=64, nargs="?", help="The number of bytes to dump.")
 @pwndbg.commands.ArgparsedCommand(parser)
 @pwndbg.commands.OnlyWhenRunning
 def db(address, count=64):
@@ -42,8 +42,8 @@ def db(address, count=64):
 
 
 parser = argparse.ArgumentParser(description="Starting at the specified address, dump N words.")
-parser.add_argument("address", type=int, help="The address to dump from.")
-parser.add_argument("count", type=int, default=32, nargs="?", help="The number of words to dump.")
+parser.add_argument("address", type=pwndbg.commands.sloppy_gdb_parse, help="The address to dump from.")
+parser.add_argument("count", type=pwndbg.commands.sloppy_gdb_parse, default=32, nargs="?", help="The number of words to dump.")
 @pwndbg.commands.ArgparsedCommand(parser)
 @pwndbg.commands.OnlyWhenRunning
 def dw(address, count=32):
@@ -55,8 +55,8 @@ def dw(address, count=32):
 
 
 parser = argparse.ArgumentParser(description="Starting at the specified address, dump N dwords.")
-parser.add_argument("address", type=int, help="The address to dump from.")
-parser.add_argument("count", type=int, default=16, nargs="?", help="The number of dwords to dump.")
+parser.add_argument("address", type=pwndbg.commands.sloppy_gdb_parse, help="The address to dump from.")
+parser.add_argument("count", type=pwndbg.commands.sloppy_gdb_parse, default=16, nargs="?", help="The number of dwords to dump.")
 @pwndbg.commands.ArgparsedCommand(parser)
 @pwndbg.commands.OnlyWhenRunning
 def dd(address, count=16):
@@ -67,8 +67,8 @@ def dd(address, count=16):
     return dX(4, address, count, repeat=dd.repeat)
 
 parser = argparse.ArgumentParser(description="Starting at the specified address, dump N qwords.")
-parser.add_argument("address", type=int, help="The address to dump from.")
-parser.add_argument("count", type=int, default=8, nargs="?", help="The number of qwords to dump.")
+parser.add_argument("address", type=pwndbg.commands.sloppy_gdb_parse, help="The address to dump from.")
+parser.add_argument("count", type=pwndbg.commands.sloppy_gdb_parse, default=8, nargs="?", help="The number of qwords to dump.")
 @pwndbg.commands.ArgparsedCommand(parser)
 @pwndbg.commands.OnlyWhenRunning
 def dq(address, count=8):
@@ -79,8 +79,8 @@ def dq(address, count=8):
     return dX(8, address, count, repeat=dq.repeat)
 
 parser = argparse.ArgumentParser(description="Starting at the specified address, hexdump.")
-parser.add_argument("address", type=int, help="The address to dump from.")
-parser.add_argument("count", type=int, default=8, nargs="?", help="The number of bytes to hexdump.")
+parser.add_argument("address", type=pwndbg.commands.sloppy_gdb_parse, help="The address to dump from.")
+parser.add_argument("count", type=pwndbg.commands.sloppy_gdb_parse, default=8, nargs="?", help="The number of bytes to hexdump.")
 @pwndbg.commands.ArgparsedCommand(parser)
 @pwndbg.commands.OnlyWhenRunning
 def dc(address, count=8):
@@ -164,7 +164,7 @@ def ew(address, data):
 
 
 parser = argparse.ArgumentParser(description="Write hex dwords at the specified address.")
-parser.add_argument("address", type=int, help="The address to write to.")
+parser.add_argument("address", type=pwndbg.commands.sloppy_gdb_parse, help="The address to write to.")
 parser.add_argument("data", type=str, nargs="*", help="The dwords to write.")
 @pwndbg.commands.ArgparsedCommand(parser)
 @pwndbg.commands.OnlyWhenRunning
@@ -176,7 +176,7 @@ def ed(address, data):
 
 
 parser = argparse.ArgumentParser(description="Write hex qwords at the specified address.")
-parser.add_argument("address", type=int, help="The address to write to.")
+parser.add_argument("address", type=pwndbg.commands.sloppy_gdb_parse, help="The address to write to.")
 parser.add_argument("data", type=str, nargs="*", help="The qwords to write.")
 @pwndbg.commands.ArgparsedCommand(parser)
 @pwndbg.commands.OnlyWhenRunning
@@ -188,7 +188,7 @@ def eq(address, data):
 
 
 parser = argparse.ArgumentParser(description="Write a string at the specified address.")
-parser.add_argument("address", type=int, help="The address to write to.")
+parser.add_argument("address", type=pwndbg.commands.sloppy_gdb_parse, help="The address to write to.")
 parser.add_argument("data", type=str, help="The string to write.")
 @pwndbg.commands.ArgparsedCommand(parser)
 @pwndbg.commands.OnlyWhenRunning
@@ -199,7 +199,7 @@ def ez(address, data):
     return eX(1, address, data, hex=False)
 
 parser = argparse.ArgumentParser(description="Write a string at the specified address.") #TODO Is eza just ez? If so just alias. I had trouble finding windbg documentation defining ez
-parser.add_argument("address", type=int, help="The address to write to.")
+parser.add_argument("address", type=pwndbg.commands.sloppy_gdb_parse, help="The address to write to.")
 parser.add_argument("data", type=str, help="The string to write.")
 @pwndbg.commands.ArgparsedCommand(parser)
 @pwndbg.commands.OnlyWhenRunning
@@ -237,7 +237,7 @@ def eX(size, address, data, hex=True):
         pwndbg.memory.write(address + (i * size), data)
 
 parser = argparse.ArgumentParser(description="Dump pointers and symbols at the specified address.")
-parser.add_argument("addr", type=int, help="The address to dump from.")
+parser.add_argument("addr", type=pwndbg.commands.sloppy_gdb_parse, help="The address to dump from.")
 @pwndbg.commands.ArgparsedCommand(parser,aliases=['kd','dps','dqs']) #TODO are these really all the same? They had identical implementation...
 @pwndbg.commands.OnlyWhenRunning
 def dds(addr):
@@ -249,19 +249,17 @@ def dds(addr):
 
 da_parser = argparse.ArgumentParser()
 da_parser.description = 'Dump a string at the specified address.'
-da_parser.add_argument('address', help='Address to dump')
+da_parser.add_argument('address', type=pwndbg.commands.sloppy_gdb_parse, help='Address to dump')
 da_parser.add_argument('max', type=int, nargs='?', default=256,
                        help='Maximum string length')
 @pwndbg.commands.ArgparsedCommand(da_parser)
 @pwndbg.commands.OnlyWhenRunning
 def da(address, max):
-    address = int(address)
-    address &= pwndbg.arch.ptrmask
     print("%x" % address, repr(pwndbg.strings.get(address, max)))
 
 ds_parser = argparse.ArgumentParser()
 ds_parser.description = 'Dump a string at the specified address.'
-ds_parser.add_argument('address', help='Address to dump')
+ds_parser.add_argument('address', type=pwndbg.commands.sloppy_gdb_parse, help='Address to dump')
 ds_parser.add_argument('max', type=int, nargs='?', default=256,
                        help='Maximum string length')
 @pwndbg.commands.ArgparsedCommand(ds_parser)


Test patch: