【问题标题】:Avoiding expensive calculations in Python IRC bot避免在 Python IRC bot 中进行昂贵的计算
【发布时间】:2012-07-14 19:34:20
【问题描述】:

我在公共 IRC 机器人中使用 this calculator。鉴于 Python 默认使用任意精度,这将允许任何用户执行 calc 10000**10000**10000calc factorial(1000000) 之类的操作并有效地“杀死”机器人。

我想知道是否有某种方法可以避免这种情况。我已经尝试将表达式中的所有项转换为浮动,但 float(factorial(1000000) 在 Python 解释器中仍然需要很长时间才能完成,而且我不确定多线程方法是否是正确的方法。

【问题讨论】:

  • 我知道它使用eval,但是我尝试执行危险命令已经很久了,我什么也做不了。我知道这并不能保证它是 100% 安全的,但是将要使用的用户是“半可信的”,这意味着他们不会对 os 做奇怪的事情,但我相信他们会杀了有趣的机器人。
  • 这是计算器的实际代码,我用那个代码只是为了让问题更简洁。 pastebin.com/auF1krdh
  • 对python一无所知我想你需要像Loic这样的方法:将计算器作为独立进程启动并在指定时间后将其终止。恐怕通过使用阶乘之类的功能,几乎不可能强迫用户只插入快速可计算的输入,有太多的方法可以让你的计算器在脚下射击。
  • @user1002327:请参阅此链接:eval is really dangerous。您应该改用ast.literal_eval。此外,在一个带有超时的单独线程中运行您的用户输入。这将避免像factorial(1000000) 这样的问题。
  • 呵呵,感觉好像在其他地方看到过你的评论。我已经阅读了该链接,这就是我没有让用户在集合[]_;"' 中包含任何字符的原因。我希望这会让滥用eval 变得更加困难。哦,我函数中的eval 明确使用{"__builtins__": None} 而不是{'__builtins__':{}},但我不确定它是否会有所不同。无论哪种方式,我都会使用ast.literal_eval 看看会发生什么。

标签: python calculator arbitrary-precision


【解决方案1】:

不是一个真正的答案,但我会那样做。您要运行的所有内容都应该在不同的进程中运行。据我所知,不可能限制进程中单个线程的 CPU 使用率或内存使用率。

也就是说,您必须创建一个新进程,该进程与任务一样执行用户输入的内容并将其写入文件,例如。您可以使用fork 执行此操作,使用PID 创建一个新文件,并且主进程必须检查直到子进程终止。一旦进程终止,打开文件“cool_calculator_[pid].out”并将其发送回IRC

我想这很简单。

然后使用 ulimit 或其他工具,可以限制子进程甚至使用主进程杀死它们。如果带有pid 的文件为空,只需回答存在错误或其他问题。我猜你甚至可以写一些错误,比如超出内存或超出 CPU 等。

这完全取决于您要如何杀死不良进程。

最后,您的主进程将负责生成子进程并在需要时杀死它们并返回答案。

【讨论】:

  • 哦,好吧,因为我在 C 中遇到各种溢出错误,我想知道是否可以在 Python 中检测这些溢出。正如我之前所说,将所有内容都转换为浮动是不够的,但它可以解决很多问题。 float(10000) ** float(9999999999) 抛出 OverflowErrorfloat(factorial(12000)) 但出于某种原因 float(factorial(120000)) 试图完成整个计算。
  • float(factorial(120000)) 确实 抛出一个溢出错误,它只需要一段时间。 OverflowError 发生在 计算阶乘之后。
  • 哦,我没有让它完成,因为我认为它需要更长的时间。感谢您指出这一点。
【解决方案2】:

看来float() 演员毕竟是解决方案。

首先,反三角函数不会在其域之外取值,因此它们是完全安全的,并且可以捕获异常。

>>> acos(5e100)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: math domain error

fmod() 函数也会发生同样的事情。

“正常”三角函数似乎对大值没有任何问题,除非它们真的很大,这使得函数再次返回 ValueError

舍入函数(ceil()floor()round())工作正常,如果值太大则返回 infdegrees()log()log10()pow()sqrt()fabs()hypot()radians() 函数也是如此。

双曲三角函数和exp() 函数抛出OverflowErrors 或返回inf

atan2() 函数可以很好地处理大值。

对于简单的算术运算,浮点转换使函数抛出 OverflowError(或 inf)而不是进行计算。

>>> float(10) ** float(100) ** float(100) ** float(1000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: (34, 'Numerical result out of range')
>>> float(5e500) * float(4e1000)
inf

最后,有问题的 factorial() 函数。我所要做的就是以迭代的方式重新定义函数并将其添加到safe_dict

import sys

def factorial(n):
    fact = 1
    while (n > 0):
        fact = float(fact) * float(n)
        n -= float(1)
        if float(fact) > sys.float_info.max:
            return "Too big"
    return str(fact)

print factorial(50e500)

虽然这是一种非常丑陋且效率极低的阶乘计算方式,但足以满足我的需求。其实我觉得我加了很多不必要的float()s。

现在我需要弄清楚如何将float()s 放在表达式中的所有术语周围,以便自动发生。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-29
    • 1970-01-01
    • 2011-11-01
    相关资源
    最近更新 更多